<commit_before>use crate::iter::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen};
use crate::ops::Try;
use crate::usize;

/// An iterator that links two iterators together, in a chain.
///
/// This `struct` is created by [`Iterator::chain`]. See its documentation
/// for more.
///
/// # Examples
///
/// ```
/// use std::iter::Chain;
/// use std::slice::Iter;
///
/// let a1 = [1, 2, 3];
/// let a2 = [4, 5, 6];
/// let iter: Chain<Iter<_>, Iter<_>> = a1.iter().chain(a2.iter());
/// ```
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Chain<A, B> {
    // These are "fused" with `Option` so we don't need separate state to track which part is
    // already exhausted, and we may also get niche layout for `None`. We don't use the real `Fuse`
    // adapter because its specialization for `FusedIterator` unconditionally descends into the
    // iterator, and that could be expensive to keep revisiting stuff like nested chains. It also
    // hurts compiler performance to add more iterator layers to `Chain`.
    //
    // Only the "first" iterator is actually set `None` when exhausted, depending on whether you
    // iterate forward or backward. If you mix directions, then both sides may be `None`.
    a: Option<A>,
    b: Option<B>,
}
impl<A, B> Chain<A, B> {
    pub(in super::super) fn new(a: A, b: B) -> Chain<A, B> {
        Chain { a: Some(a), b: Some(b) }
    }
}

/// Fuse the iterator if the expression is `None`.
macro_rules! fuse {
    ($self:ident . $iter:ident . $($call:tt)+) => {
        match $self.$iter {
            Some(ref mut iter) => match iter.$($call)+ {
                None => {
                    $self.$iter = None;
                    None
                }
                item => item,
            },
            None => None,
        }
    };
}

/// Try an iterator method without fusing,
/// like an inline `.as_mut().and_then(...)`
macro_rules! maybe {
    ($self:ident . $iter:ident . $($call:tt)+) => {
        match $self.$iter {
            Some(ref mut iter) => iter.$($call)+,
            None => None,
        }
    };
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> Iterator for Chain<A, B>
where
    A: Iterator,
    B: Iterator<Item = A::Item>,
{
    type Item = A::Item;

    #[inline]
    fn next(&mut self) -> Option<A::Item> {
        match fuse!(self.a.next()) {
            None => maybe!(self.b.next()),
            item => item,
        }
    }

    #[inline]
    #[rustc_inherit_overflow_checks]
    fn count(self) -> usize {
        let a_count = match self.a {
            Some(a) => a.count(),
            None => 0,
        };
        let b_count = match self.b {
            Some(b) => b.count(),
            None => 0,
        };
        a_count + b_count
    }

    fn try_fold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut a) = self.a {
            acc = a.try_fold(acc, &mut f)?;
            self.a = None;
        }
        if let Some(ref mut b) = self.b {
            acc = b.try_fold(acc, f)?;
            // we don't fuse the second iterator
        }
        Try::from_ok(acc)
    }

    fn fold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(a) = self.a {
            acc = a.fold(acc, &mut f);
        }
        if let Some(b) = self.b {
            acc = b.fold(acc, f);
        }
        acc
    }

    #[inline]
    fn nth(&mut self, mut n: usize) -> Option<A::Item> {
        if let Some(ref mut a) = self.a {
            while let Some(x) = a.next() {
                if n == 0 {
                    return Some(x);
                }
                n -= 1;
            }
            self.a = None;
        }
        maybe!(self.b.nth(n))
    }

    #[inline]
    fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        match fuse!(self.a.find(&mut predicate)) {
            None => maybe!(self.b.find(predicate)),
            item => item,
        }
    }

    #[inline]
    fn last(self) -> Option<A::Item> {
        // Must exhaust a before b.
        let a_last = match self.a {
            Some(a) => a.last(),
            None => None,
        };
        let b_last = match self.b {
            Some(b) => b.last(),
            None => None,
        };
        b_last.or(a_last)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match self {
            Chain { a: Some(a), b: Some(b) } => {
                let (a_lower, a_upper) = a.size_hint();
                let (b_lower, b_upper) = b.size_hint();

                let lower = a_lower.saturating_add(b_lower);

                let upper = match (a_upper, b_upper) {
                    (Some(x), Some(y)) => x.checked_add(y),
                    _ => None,
                };

                (lower, upper)
            }
            Chain { a: Some(a), b: None } => a.size_hint(),
            Chain { a: None, b: Some(b) } => b.size_hint(),
            Chain { a: None, b: None } => (0, Some(0)),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> DoubleEndedIterator for Chain<A, B>
where
    A: DoubleEndedIterator,
    B: DoubleEndedIterator<Item = A::Item>,
{
    #[inline]
    fn next_back(&mut self) -> Option<A::Item> {
        match fuse!(self.b.next_back()) {
            None => maybe!(self.a.next_back()),
            item => item,
        }
    }

    #[inline]
    fn nth_back(&mut self, mut n: usize) -> Option<A::Item> {
        if let Some(ref mut b) = self.b {
            while let Some(x) = b.next_back() {
                if n == 0 {
                    return Some(x);
                }
                n -= 1;
            }
            self.b = None;
        }
        maybe!(self.a.nth_back(n))
    }

    #[inline]
    fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        match fuse!(self.b.rfind(&mut predicate)) {
            None => maybe!(self.a.rfind(predicate)),
            item => item,
        }
    }

    fn try_rfold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut b) = self.b {
            acc = b.try_rfold(acc, &mut f)?;
            self.b = None;
        }
        if let Some(ref mut a) = self.a {
            acc = a.try_rfold(acc, f)?;
            // we don't fuse the second iterator
        }
        Try::from_ok(acc)
    }

    fn rfold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(b) = self.b {
            acc = b.rfold(acc, &mut f);
        }
        if let Some(a) = self.a {
            acc = a.rfold(acc, f);
        }
        acc
    }
}

// Note: *both* must be fused to handle double-ended iterators.
#[stable(feature = "fused", since = "1.26.0")]
impl<A, B> FusedIterator for Chain<A, B>
where
    A: FusedIterator,
    B: FusedIterator<Item = A::Item>,
{
}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<A, B> TrustedLen for Chain<A, B>
where
    A: TrustedLen,
    B: TrustedLen<Item = A::Item>,
{
}
<commit_msg>Remove trailing whitespace in iter chain doc<commit_after>use crate::iter::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen};
use crate::ops::Try;
use crate::usize;

/// An iterator that links two iterators together, in a chain.
///
/// This `struct` is created by [`Iterator::chain`]. See its documentation
/// for more.
///
/// # Examples
///
/// ```
/// use std::iter::Chain;
/// use std::slice::Iter;
///
/// let a1 = [1, 2, 3];
/// let a2 = [4, 5, 6];
/// let iter: Chain<Iter<_>, Iter<_>> = a1.iter().chain(a2.iter());
/// ```
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Chain<A, B> {
    // These are "fused" with `Option` so we don't need separate state to track which part is
    // already exhausted, and we may also get niche layout for `None`. We don't use the real `Fuse`
    // adapter because its specialization for `FusedIterator` unconditionally descends into the
    // iterator, and that could be expensive to keep revisiting stuff like nested chains. It also
    // hurts compiler performance to add more iterator layers to `Chain`.
    //
    // Only the "first" iterator is actually set `None` when exhausted, depending on whether you
    // iterate forward or backward. If you mix directions, then both sides may be `None`.
    a: Option<A>,
    b: Option<B>,
}
impl<A, B> Chain<A, B> {
    pub(in super::super) fn new(a: A, b: B) -> Chain<A, B> {
        Chain { a: Some(a), b: Some(b) }
    }
}

/// Fuse the iterator if the expression is `None`.
macro_rules! fuse {
    ($self:ident . $iter:ident . $($call:tt)+) => {
        match $self.$iter {
            Some(ref mut iter) => match iter.$($call)+ {
                None => {
                    $self.$iter = None;
                    None
                }
                item => item,
            },
            None => None,
        }
    };
}

/// Try an iterator method without fusing,
/// like an inline `.as_mut().and_then(...)`
macro_rules! maybe {
    ($self:ident . $iter:ident . $($call:tt)+) => {
        match $self.$iter {
            Some(ref mut iter) => iter.$($call)+,
            None => None,
        }
    };
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> Iterator for Chain<A, B>
where
    A: Iterator,
    B: Iterator<Item = A::Item>,
{
    type Item = A::Item;

    #[inline]
    fn next(&mut self) -> Option<A::Item> {
        match fuse!(self.a.next()) {
            None => maybe!(self.b.next()),
            item => item,
        }
    }

    #[inline]
    #[rustc_inherit_overflow_checks]
    fn count(self) -> usize {
        let a_count = match self.a {
            Some(a) => a.count(),
            None => 0,
        };
        let b_count = match self.b {
            Some(b) => b.count(),
            None => 0,
        };
        a_count + b_count
    }

    fn try_fold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut a) = self.a {
            acc = a.try_fold(acc, &mut f)?;
            self.a = None;
        }
        if let Some(ref mut b) = self.b {
            acc = b.try_fold(acc, f)?;
            // we don't fuse the second iterator
        }
        Try::from_ok(acc)
    }

    fn fold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(a) = self.a {
            acc = a.fold(acc, &mut f);
        }
        if let Some(b) = self.b {
            acc = b.fold(acc, f);
        }
        acc
    }

    #[inline]
    fn nth(&mut self, mut n: usize) -> Option<A::Item> {
        if let Some(ref mut a) = self.a {
            while let Some(x) = a.next() {
                if n == 0 {
                    return Some(x);
                }
                n -= 1;
            }
            self.a = None;
        }
        maybe!(self.b.nth(n))
    }

    #[inline]
    fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        match fuse!(self.a.find(&mut predicate)) {
            None => maybe!(self.b.find(predicate)),
            item => item,
        }
    }

    #[inline]
    fn last(self) -> Option<A::Item> {
        // Must exhaust a before b.
        let a_last = match self.a {
            Some(a) => a.last(),
            None => None,
        };
        let b_last = match self.b {
            Some(b) => b.last(),
            None => None,
        };
        b_last.or(a_last)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match self {
            Chain { a: Some(a), b: Some(b) } => {
                let (a_lower, a_upper) = a.size_hint();
                let (b_lower, b_upper) = b.size_hint();

                let lower = a_lower.saturating_add(b_lower);

                let upper = match (a_upper, b_upper) {
                    (Some(x), Some(y)) => x.checked_add(y),
                    _ => None,
                };

                (lower, upper)
            }
            Chain { a: Some(a), b: None } => a.size_hint(),
            Chain { a: None, b: Some(b) } => b.size_hint(),
            Chain { a: None, b: None } => (0, Some(0)),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> DoubleEndedIterator for Chain<A, B>
where
    A: DoubleEndedIterator,
    B: DoubleEndedIterator<Item = A::Item>,
{
    #[inline]
    fn next_back(&mut self) -> Option<A::Item> {
        match fuse!(self.b.next_back()) {
            None => maybe!(self.a.next_back()),
            item => item,
        }
    }

    #[inline]
    fn nth_back(&mut self, mut n: usize) -> Option<A::Item> {
        if let Some(ref mut b) = self.b {
            while let Some(x) = b.next_back() {
                if n == 0 {
                    return Some(x);
                }
                n -= 1;
            }
            self.b = None;
        }
        maybe!(self.a.nth_back(n))
    }

    #[inline]
    fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        P: FnMut(&Self::Item) -> bool,
    {
        match fuse!(self.b.rfind(&mut predicate)) {
            None => maybe!(self.a.rfind(predicate)),
            item => item,
        }
    }

    fn try_rfold<Acc, F, R>(&mut self, mut acc: Acc, mut f: F) -> R
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> R,
        R: Try<Ok = Acc>,
    {
        if let Some(ref mut b) = self.b {
            acc = b.try_rfold(acc, &mut f)?;
            self.b = None;
        }
        if let Some(ref mut a) = self.a {
            acc = a.try_rfold(acc, f)?;
            // we don't fuse the second iterator
        }
        Try::from_ok(acc)
    }

    fn rfold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        if let Some(b) = self.b {
            acc = b.rfold(acc, &mut f);
        }
        if let Some(a) = self.a {
            acc = a.rfold(acc, f);
        }
        acc
    }
}

// Note: *both* must be fused to handle double-ended iterators.
#[stable(feature = "fused", since = "1.26.0")]
impl<A, B> FusedIterator for Chain<A, B>
where
    A: FusedIterator,
    B: FusedIterator<Item = A::Item>,
{
}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<A, B> TrustedLen for Chain<A, B>
where
    A: TrustedLen,
    B: TrustedLen<Item = A::Item>,
{
}
<|endoftext|>


<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 rand::Rng;
use crate::{Distribution, Cauchy};
use crate::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_distr::{Binomial, Distribution};
///
/// let bin = Binomial::new(20, 0.3).unwrap();
/// 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,
}

/// Error type returned from `Binomial::new`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
    /// `p < 0` or `nan`.
    ProbabilityTooSmall,
    /// `p > 1`.
    ProbabilityTooLarge,
}

impl Binomial {
    /// Construct a new `Binomial` with the given shape parameters `n` (number
    /// of trials) and `p` (probability of success).
    pub fn new(n: u64, p: f64) -> Result<Binomial, Error> {
        if !(p >= 0.0) {
            return Err(Error::ProbabilityTooSmall);
        }
        if !(p <= 1.0) {
            return Err(Error::ProbabilityTooLarge);
        }
        Ok(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).unwrap();
            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 rand::Rng;
    use crate::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).unwrap();

        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 = crate::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 = crate::test::rng(352);
        assert_eq!(rng.sample(Binomial::new(20, 0.0).unwrap()), 0);
        assert_eq!(rng.sample(Binomial::new(20, 1.0).unwrap()), 20);
    }

    #[test]
    #[should_panic]
    fn test_binomial_invalid_lambda_neg() {
        Binomial::new(20, -10.0).unwrap();
    }
}
<commit_msg>rand_distr: Update binomial distribution implementation<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 rand::Rng;
use crate::{Distribution, Uniform};

/// 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_distr::{Binomial, Distribution};
///
/// let bin = Binomial::new(20, 0.3).unwrap();
/// 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,
}

/// Error type returned from `Binomial::new`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
    /// `p < 0` or `nan`.
    ProbabilityTooSmall,
    /// `p > 1`.
    ProbabilityTooLarge,
}

impl Binomial {
    /// Construct a new `Binomial` with the given shape parameters `n` (number
    /// of trials) and `p` (probability of success).
    pub fn new(n: u64, p: f64) -> Result<Binomial, Error> {
        if !(p >= 0.0) {
            return Err(Error::ProbabilityTooSmall);
        }
        if !(p <= 1.0) {
            return Err(Error::ProbabilityTooLarge);
        }
        Ok(Binomial { n, p })
    }
}

/// Convert a `f64` to an `i64`, panicing on overflow.
// In the future (Rust 1.34), this might be replaced with `TryFrom`.
fn f64_to_i64(x: f64) -> i64 {
    assert!(x < (::std::i64::MAX as f64));
    x as i64
}

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;
        }

        // The 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;
        let q = 1. - p;

        // For small n * min(p, 1 - p), the BINV algorithm based on the inverse
        // transformation of the binomial distribution is efficient. Otherwise,
        // the BTPE algorithm is used.
        //
        // 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

        // Threshold for prefering the BINV algorithm. The paper suggests 10,
        // Ranlib uses 30, and GSL uses 14.
        const BINV_THRESHOLD: f64 = 10.;

        if (self.n as f64) * p < BINV_THRESHOLD &&
           self.n <= (::std::i32::MAX as u64) {
            // Use the BINV algorithm.
            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 {
            // Use the BTPE algorithm.

            // Threshold for using the squeeze algorithm. This can be freely
            // chosen based on performance. Ranlib and GSL use 20.
            const SQUEEZE_THRESHOLD: i64 = 20;

            // Step 0: Calculate constants as functions of `n` and `p`.
            let n = self.n as f64;
            let np = n * p;
            let npq = np * q;
            let f_m = np + p;
            let m = f64_to_i64(f_m);
            // radius of triangle region, since height=1 also area of region
            let p1 = (2.195 * npq.sqrt() - 4.6 * q).floor() + 0.5;
            // tip of triangle
            let x_m = (m as f64) + 0.5;
            // left edge of triangle
            let x_l = x_m - p1;
            // right edge of triangle
            let x_r = x_m + p1;
            let c = 0.134 + 20.5 / (15.3 + (m as f64));
            // p1 + area of parallelogram region
            let p2 = p1 * (1. + 2. * c);

            fn lambda(a: f64) -> f64 {
                a * (1. + 0.5 * a)
            }

            let lambda_l = lambda((f_m - x_l) / (f_m - x_l * p));
            let lambda_r = lambda((x_r - f_m) / (x_r * q));
            // p1 + area of left tail
            let p3 = p2 + c / lambda_l;
            // p1 + area of right tail
            let p4 = p3 + c / lambda_r;

            // return value
            let mut y: i64;

            let gen_u = Uniform::new(0., p4);
            let gen_v = Uniform::new(0., 1.);

            loop {
                // Step 1: Generate `u` for selecting the region. If region 1 is
                // selected, generate a triangularly distributed variate.
                let u = gen_u.sample(rng);
                let mut v = gen_v.sample(rng);
                if !(u > p1) {
                    y = f64_to_i64(x_m - p1 * v + u);
                    break;
                }

                if !(u > p2) {
                    // Step 2: Region 2, parallelograms. Check if region 2 is
                    // used. If so, generate `y`.
                    let x = x_l + (u - p1) / c;
                    v = v * c + 1.0 - (x - x_m).abs() / p1;
                    if v > 1. {
                        continue;
                    } else {
                        y = f64_to_i64(x);
                    }
                } else if !(u > p3) {
                    // Step 3: Region 3, left exponential tail.
                    y = f64_to_i64(x_l + v.ln() / lambda_l);
                    if y < 0 {
                        continue;
                    } else {
                        v *= (u - p2) * lambda_l;
                    }
                } else {
                    // Step 4: Region 4, right exponential tail.
                    y = f64_to_i64(x_r - v.ln() / lambda_r);
                    if y > 0 && (y as u64) > self.n {
                        continue;
                    } else {
                        v *= (u - p3) * lambda_r;
                    }
                }

                // Step 5: Acceptance/rejection comparison.

                // Step 5.0: Test for appropriate method of evaluating f(y).
                let k = (y - m).abs();
                if !(k > SQUEEZE_THRESHOLD && (k as f64) < 0.5 * npq - 1.) {
                    // Step 5.1: Evaluate f(y) via the recursive relationship. Start the
                    // search from the mode.
                    let s = p / q;
                    let a = s * (n + 1.);
                    let mut f = 1.0;
                    if m < y {
                        let mut i = m;
                        loop {
                            i += 1;
                            f *= a / (i as f64) - s;
                            if i == y {
                                break;
                            }
                        }
                    } else if m > y {
                        let mut i = y;
                        loop {
                            i += 1;
                            f /= a / (i as f64) - s;
                            if i == m {
                                break;
                            }
                        }
                    }
                    if v > f {
                        continue;
                    } else {
                        break;
                    }
                }

                // Step 5.2: Squeezing. Check the value of ln(v) againts upper and
                // lower bound of ln(f(y)).
                let k = k as f64;
                let rho = (k / npq) * ((k * (k / 3. + 0.625) + 1./6.) / npq + 0.5);
                let t = -0.5 * k*k / npq;
                let alpha = v.ln();
                if alpha < t - rho {
                    break;
                }
                if alpha > t + rho {
                    continue;
                }

                // Step 5.3: Final acceptance/rejection test.
                let x1 = (y + 1) as f64;
                let f1 = (m + 1) as f64;
                let z = (f64_to_i64(n) + 1 - m) as f64;
                let w = (f64_to_i64(n) - y + 1) as f64;

                fn stirling(a: f64) -> f64 {
                    let a2 = a * a;
                    (13860. - (462. - (132. - (99. - 140. / a2) / a2) / a2) / a2) / a / 166320.
                }

                if alpha > x_m * (f1 / x1).ln()
                    + (n - (m as f64) + 0.5) * (z / w).ln()
                    + ((y - m) as f64) * (w * p / (x1 * q)).ln()
                    // We use the signs from the GSL implementation, which are
                    // different than the ones in the reference. According to
                    // the GSL authors, the new signs were verified to be
                    // correct by one of the original designers of the
                    // algorithm.
                    + stirling(f1) + stirling(z) - stirling(x1) - stirling(w)
                {
                    continue;
                }

                break;
            }
            assert!(y >= 0);
            result = y as u64;
        }

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

#[cfg(test)]
mod test {
    use rand::Rng;
    use crate::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).unwrap();

        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 = crate::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 = crate::test::rng(352);
        assert_eq!(rng.sample(Binomial::new(20, 0.0).unwrap()), 0);
        assert_eq!(rng.sample(Binomial::new(20, 1.0).unwrap()), 20);
    }

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


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

use {Intrinsic, i, f, v};
use rustc::middle::ty;

macro_rules! p {
    ($name: expr, ($($inputs: tt),*) -> $output: tt) => {
        plain!(concat!("llvm.x86.", $name), ($($inputs),*) -> $output)
    }
}

pub fn find<'tcx>(_tcx: &ty::ctxt<'tcx>, name: &str) -> Option<Intrinsic> {
    if !name.starts_with("mm_") { return None }

    Some(match &name["mm_".len()..] {
        "sqrt_ps" => plain!("llvm.sqrt.v4f32", (f32x4) -> f32x4),
        "sqrt_pd" => plain!("llvm.sqrt.v2f64", (f64x2) -> f64x2),

        "movemask_ps" => p!("sse.movmsk.ps", (f32x4) -> i32),
        "max_ps" => p!("sse.max.ps", (f32x4, f32x4) -> f32x4),
        "min_ps" => p!("sse.min.ps", (f32x4, f32x4) -> f32x4),
        "rsqrt_ps" => p!("sse.rsqrt.ps", (f32x4) -> f32x4),
        "rcp_ps" => p!("sse.rcp.ps", (f32x4) -> f32x4),

        "adds_epi16" => p!("sse2.padds.w", (i16x8, i16x8) -> i16x8),
        "adds_epi8" => p!("sse2.padds.b", (i8x16, i8x16) -> i8x16),
        "adds_epu16" => p!("sse2.paddus.w", (i16x8, i16x8) -> i16x8),
        "adds_epu8" => p!("sse2.paddus.b", (i8x16, i8x16) -> i8x16),
        "avg_epu16" => p!("sse2.pavg.w", (i16x8, i16x8) -> i16x8),
        "avg_epu8" => p!("sse2.pavg.b", (i8x16, i8x16) -> i8x16),
        "madd_epi16" => p!("sse2.pmadd.wd", (i16x8, i16x8) -> i32x4),
        "max_epi16" => p!("sse2.pmaxs.w", (i16x8, i16x8) -> i16x8),
        "max_epu8" => p!("sse2.pmaxu.b", (i8x16, i8x16) -> i8x16),
        "max_pd" => p!("sse2.max.pd", (f64x2, f64x2) -> f64x2),
        "min_epi16" => p!("sse2.pmins.w", (i16x8, i16x8) -> i16x8),
        "min_epu8" => p!("sse2.pminu.b", (i8x16, i8x16) -> i8x16),
        "min_pd" => p!("sse2.min.pd", (f64x2, f64x2) -> f64x2),
        "movemask_pd" => p!("sse2.movmsk.pd", (f64x2) -> i32),
        "movemask_epi8" => p!("sse2.pmovmskb.128", (i8x16) -> i32),
        "mul_epu32" => p!("sse2.pmulu.dq", (i32x4, i32x4) -> i64x2),
        "mulhi_epi16" => p!("sse2.pmulh.w", (i8x16, i8x16) -> i8x16),
        "mulhi_epu16" => p!("sse2.pmulhu.w", (i8x16, i8x16) -> i8x16),
        "packs_epi16" => p!("sse2.packsswb.128", (i16x8, i16x8) -> i8x16),
        "packs_epi32" => p!("sse2.packssdw.128", (i32x4, i32x4) -> i16x8),
        "packus_epi16" => p!("sse2.packuswb.128", (i16x8, i16x8) -> i8x16),
        "sad_epu8" => p!("sse2.psad.bw", (i8x16, i8x16) -> i64x2),
        "subs_epi16" => p!("sse2.psubs.w", (i16x8, i16x8) -> i16x8),
        "subs_epi8" => p!("sse2.psubs.b", (i8x16, i8x16) -> i8x16),
        "subs_epu16" => p!("sse2.psubus.w", (i16x8, i16x8) -> i16x8),
        "subs_epu8" => p!("sse2.psubus.b", (i8x16, i8x16) -> i8x16),

        "addsub_pd" => p!("sse3.addsub.pd", (f64x2, f64x2) -> f64x2),
        "addsub_ps" => p!("sse3.addsub.ps", (f32x4, f32x4) -> f32x4),
        "hadd_pd" => p!("sse3.hadd.pd", (f64x2, f64x2) -> f64x2),
        "hadd_ps" => p!("sse3.hadd.ps", (f32x4, f32x4) -> f32x4),
        "hsub_pd" => p!("sse3.hsub.pd", (f64x2, f64x2) -> f64x2),
        "hsub_ps" => p!("sse3.hsub.ps", (f32x4, f32x4) -> f32x4),

        "shuffle_epi8" => p!("ssse3.pshuf.b.128", (i8x16, i8x16) -> i8x16),
        _ => return None
    })
}
<commit_msg>Add most SSSE3 intrinsics.<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use {Intrinsic, i, f, v};
use rustc::middle::ty;

macro_rules! p {
    ($name: expr, ($($inputs: tt),*) -> $output: tt) => {
        plain!(concat!("llvm.x86.", $name), ($($inputs),*) -> $output)
    }
}

pub fn find<'tcx>(_tcx: &ty::ctxt<'tcx>, name: &str) -> Option<Intrinsic> {
    if !name.starts_with("mm_") { return None }

    Some(match &name["mm_".len()..] {
        "sqrt_ps" => plain!("llvm.sqrt.v4f32", (f32x4) -> f32x4),
        "sqrt_pd" => plain!("llvm.sqrt.v2f64", (f64x2) -> f64x2),

        "movemask_ps" => p!("sse.movmsk.ps", (f32x4) -> i32),
        "max_ps" => p!("sse.max.ps", (f32x4, f32x4) -> f32x4),
        "min_ps" => p!("sse.min.ps", (f32x4, f32x4) -> f32x4),
        "rsqrt_ps" => p!("sse.rsqrt.ps", (f32x4) -> f32x4),
        "rcp_ps" => p!("sse.rcp.ps", (f32x4) -> f32x4),

        "adds_epi16" => p!("sse2.padds.w", (i16x8, i16x8) -> i16x8),
        "adds_epi8" => p!("sse2.padds.b", (i8x16, i8x16) -> i8x16),
        "adds_epu16" => p!("sse2.paddus.w", (i16x8, i16x8) -> i16x8),
        "adds_epu8" => p!("sse2.paddus.b", (i8x16, i8x16) -> i8x16),
        "avg_epu16" => p!("sse2.pavg.w", (i16x8, i16x8) -> i16x8),
        "avg_epu8" => p!("sse2.pavg.b", (i8x16, i8x16) -> i8x16),
        "madd_epi16" => p!("sse2.pmadd.wd", (i16x8, i16x8) -> i32x4),
        "max_epi16" => p!("sse2.pmaxs.w", (i16x8, i16x8) -> i16x8),
        "max_epu8" => p!("sse2.pmaxu.b", (i8x16, i8x16) -> i8x16),
        "max_pd" => p!("sse2.max.pd", (f64x2, f64x2) -> f64x2),
        "min_epi16" => p!("sse2.pmins.w", (i16x8, i16x8) -> i16x8),
        "min_epu8" => p!("sse2.pminu.b", (i8x16, i8x16) -> i8x16),
        "min_pd" => p!("sse2.min.pd", (f64x2, f64x2) -> f64x2),
        "movemask_pd" => p!("sse2.movmsk.pd", (f64x2) -> i32),
        "movemask_epi8" => p!("sse2.pmovmskb.128", (i8x16) -> i32),
        "mul_epu32" => p!("sse2.pmulu.dq", (i32x4, i32x4) -> i64x2),
        "mulhi_epi16" => p!("sse2.pmulh.w", (i8x16, i8x16) -> i8x16),
        "mulhi_epu16" => p!("sse2.pmulhu.w", (i8x16, i8x16) -> i8x16),
        "packs_epi16" => p!("sse2.packsswb.128", (i16x8, i16x8) -> i8x16),
        "packs_epi32" => p!("sse2.packssdw.128", (i32x4, i32x4) -> i16x8),
        "packus_epi16" => p!("sse2.packuswb.128", (i16x8, i16x8) -> i8x16),
        "sad_epu8" => p!("sse2.psad.bw", (i8x16, i8x16) -> i64x2),
        "subs_epi16" => p!("sse2.psubs.w", (i16x8, i16x8) -> i16x8),
        "subs_epi8" => p!("sse2.psubs.b", (i8x16, i8x16) -> i8x16),
        "subs_epu16" => p!("sse2.psubus.w", (i16x8, i16x8) -> i16x8),
        "subs_epu8" => p!("sse2.psubus.b", (i8x16, i8x16) -> i8x16),

        "addsub_pd" => p!("sse3.addsub.pd", (f64x2, f64x2) -> f64x2),
        "addsub_ps" => p!("sse3.addsub.ps", (f32x4, f32x4) -> f32x4),
        "hadd_pd" => p!("sse3.hadd.pd", (f64x2, f64x2) -> f64x2),
        "hadd_ps" => p!("sse3.hadd.ps", (f32x4, f32x4) -> f32x4),
        "hsub_pd" => p!("sse3.hsub.pd", (f64x2, f64x2) -> f64x2),
        "hsub_ps" => p!("sse3.hsub.ps", (f32x4, f32x4) -> f32x4),

        "abs_epi16" => p!("ssse3.pabs.w.128", (i16x8) -> i16x8),
        "abs_epi32" => p!("ssse3.pabs.d.128", (i32x4) -> i32x4),
        "abs_epi8" => p!("ssse3.pabs.b.128", (i8x16) -> i8x16),
        "hadd_epi16" => p!("ssse3.phadd.w.128", (i16x8, i16x8) -> i16x8),
        "hadd_epi32" => p!("ssse3.phadd.d.128", (i32x4, i32x4) -> i32x4),
        "hadds_epi16" => p!("ssse3.phadd.sw.128", (i16x8, i16x8) -> i16x8),
        "hsub_epi16" => p!("ssse3.phsub.w.128", (i16x8, i16x8) -> i16x8),
        "hsub_epi32" => p!("ssse3.phsub.d.128", (i32x4, i32x4) -> i32x4),
        "hsubs_epi16" => p!("ssse3.phsub.sw.128", (i16x8, i16x8) -> i16x8),
        "maddubs_epi16" => p!("ssse3.pmadd.ub.sw.128", (i8x16, i8x16) -> i16x8),
        "mulhrs_epi16" => p!("ssse3.pmul.hr.sw.128", (i16x8, i16x8) -> i16x8),
        "shuffle_epi8" => p!("ssse3.pshuf.b.128", (i8x16, i8x16) -> i8x16),
        "sign_epi16" => p!("ssse3.psign.w.128", (i16x8, i16x8) -> i16x8),
        "sign_epi32" => p!("ssse3.psign.d.128", (i32x4, i32x4) -> i32x4),
        "sign_epi8" => p!("ssse3.psign.b.128", (i8x16, i8x16) -> i8x16),
        _ => return None
    })
}
<|endoftext|>


<commit_before>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! rutabaga_2d: Handles 2D virtio-gpu hypercalls.

use std::cmp::{max, min};

use data_model::*;

use crate::rutabaga_core::{Rutabaga2DInfo, RutabagaComponent, RutabagaResource};
use crate::rutabaga_utils::*;

/// Transfers a resource from potentially many chunked src VolatileSlices to a dst VolatileSlice.
pub fn transfer_2d<'a, S: Iterator<Item = VolatileSlice<'a>>>(
    resource_w: u32,
    resource_h: u32,
    rect_x: u32,
    rect_y: u32,
    rect_w: u32,
    rect_h: u32,
    dst_stride: u32,
    dst_offset: u64,
    dst: VolatileSlice,
    src_stride: u32,
    src_offset: u64,
    mut srcs: S,
) -> RutabagaResult<()> {
    if rect_w == 0 || rect_h == 0 {
        return Ok(());
    }

    checked_range!(checked_arithmetic!(rect_x + rect_w)?; <= resource_w)?;
    checked_range!(checked_arithmetic!(rect_y + rect_h)?; <= resource_h)?;

    let bytes_per_pixel = 4u64;

    let rect_x = rect_x as u64;
    let rect_y = rect_y as u64;
    let rect_w = rect_w as u64;
    let rect_h = rect_h as u64;

    let dst_stride = dst_stride as u64;
    let dst_offset = dst_offset as u64;
    let dst_resource_offset = dst_offset + (rect_y * dst_stride) + (rect_x * bytes_per_pixel);

    let src_stride = src_stride as u64;
    let src_offset = src_offset as u64;
    let src_resource_offset = src_offset + (rect_y * src_stride) + (rect_x * bytes_per_pixel);

    let mut next_src;
    let mut next_line;
    let mut current_height = 0u64;
    let mut src_opt = srcs.next();

    // Cumulative start offset of the current src.
    let mut src_start_offset = 0u64;
    while let Some(src) = src_opt {
        if current_height >= rect_h {
            break;
        }

        let src_size = src.size() as u64;

        // Cumulative end offset of the current src.
        let src_end_offset = checked_arithmetic!(src_start_offset + src_size)?;

        let src_line_vertical_offset = checked_arithmetic!(current_height * src_stride)?;
        let src_line_horizontal_offset = checked_arithmetic!(rect_w * bytes_per_pixel)?;

        // Cumulative start/end offsets of the next line to copy within all srcs.
        let src_line_start_offset =
            checked_arithmetic!(src_resource_offset + src_line_vertical_offset)?;
        let src_line_end_offset =
            checked_arithmetic!(src_line_start_offset + src_line_horizontal_offset)?;

        // Clamp the line start/end offset to be inside the current src.
        let src_copyable_start_offset = max(src_line_start_offset, src_start_offset);
        let src_copyable_end_offset = min(src_line_end_offset, src_end_offset);

        if src_copyable_start_offset < src_copyable_end_offset {
            let copyable_size =
                checked_arithmetic!(src_copyable_end_offset - src_copyable_start_offset)?;

            let offset_within_src = src_copyable_start_offset.saturating_sub(src_start_offset);

            if src_line_end_offset > src_end_offset {
                next_src = true;
                next_line = false;
            } else if src_line_end_offset == src_end_offset {
                next_src = true;
                next_line = true;
            } else {
                next_src = false;
                next_line = true;
            }

            let src_subslice = src.get_slice(offset_within_src as usize, copyable_size as usize)?;

            let dst_line_vertical_offset = checked_arithmetic!(current_height * dst_stride)?;
            let dst_line_horizontal_offset =
                checked_arithmetic!(src_copyable_start_offset - src_line_start_offset)?;
            let dst_line_offset =
                checked_arithmetic!(dst_line_vertical_offset + dst_line_horizontal_offset)?;
            let dst_start_offset = checked_arithmetic!(dst_resource_offset + dst_line_offset)?;

            let dst_subslice = dst.get_slice(dst_start_offset as usize, copyable_size as usize)?;

            src_subslice.copy_to_volatile_slice(dst_subslice);
        } else {
            if src_line_start_offset >= src_start_offset {
                next_src = true;
                next_line = false;
            } else {
                next_src = false;
                next_line = true;
            }
        };

        if next_src {
            src_start_offset = checked_arithmetic!(src_start_offset + src_size)?;
            src_opt = srcs.next();
        }

        if next_line {
            current_height += 1;
        }
    }

    Ok(())
}

pub struct Rutabaga2D {
    latest_created_fence_id: u32,
}

impl Rutabaga2D {
    pub fn init() -> RutabagaResult<Box<dyn RutabagaComponent>> {
        Ok(Box::new(Rutabaga2D {
            latest_created_fence_id: 0,
        }))
    }
}

impl RutabagaComponent for Rutabaga2D {
    fn create_fence(&mut self, fence_data: RutabagaFenceData) -> RutabagaResult<()> {
        self.latest_created_fence_id = fence_data.fence_id as u32;
        Ok(())
    }

    fn poll(&self) -> u32 {
        self.latest_created_fence_id
    }

    fn create_3d(
        &self,
        resource_id: u32,
        resource_create_3d: ResourceCreate3D,
    ) -> RutabagaResult<RutabagaResource> {
        // All virtio formats are 4 bytes per pixel.
        let resource_bpp = 4;
        let resource_stride = resource_bpp * resource_create_3d.width;
        let resource_size = (resource_stride as usize) * (resource_create_3d.height as usize);
        let info_2d = Rutabaga2DInfo {
            width: resource_create_3d.width,
            height: resource_create_3d.height,
            host_mem: vec![0; resource_size],
        };

        Ok(RutabagaResource {
            resource_id,
            handle: None,
            blob: false,
            blob_mem: 0,
            blob_flags: 0,
            map_info: None,
            info_2d: Some(info_2d),
            info_3d: None,
            vulkan_info: None,
            backing_iovecs: None,
        })
    }

    fn transfer_write(
        &self,
        _ctx_id: u32,
        resource: &mut RutabagaResource,
        transfer: Transfer3D,
    ) -> RutabagaResult<()> {
        if transfer.is_empty() {
            return Ok(());
        }

        let mut info_2d = resource.info_2d.take().ok_or(RutabagaError::Unsupported)?;

        let iovecs = resource
            .backing_iovecs
            .take()
            .ok_or(RutabagaError::Unsupported)?;

        // All offical virtio_gpu formats are 4 bytes per pixel.
        let resource_bpp = 4;
        let mut src_slices = Vec::with_capacity(iovecs.len());
        for iovec in &iovecs {
            // Safe because Rutabaga users should have already checked the iovecs.
            let slice = unsafe { VolatileSlice::from_raw_parts(iovec.base as *mut u8, iovec.len) };
            src_slices.push(slice);
        }

        let src_stride = resource_bpp * info_2d.width;
        let src_offset = transfer.offset;

        let dst_stride = resource_bpp * info_2d.width;
        let dst_offset = 0;

        transfer_2d(
            info_2d.width,
            info_2d.height,
            transfer.x,
            transfer.y,
            transfer.w,
            transfer.h,
            dst_stride,
            dst_offset,
            VolatileSlice::new(info_2d.host_mem.as_mut_slice()),
            src_stride,
            src_offset,
            src_slices.iter().cloned(),
        )?;

        resource.info_2d = Some(info_2d);
        resource.backing_iovecs = Some(iovecs);
        Ok(())
    }

    fn transfer_read(
        &self,
        _ctx_id: u32,
        resource: &mut RutabagaResource,
        transfer: Transfer3D,
        buf: Option<VolatileSlice>,
    ) -> RutabagaResult<()> {
        let mut info_2d = resource.info_2d.take().ok_or(RutabagaError::Unsupported)?;

        // All offical virtio_gpu formats are 4 bytes per pixel.
        let resource_bpp = 4;
        let src_stride = resource_bpp * info_2d.width;
        let src_offset = 0;
        let dst_offset = 0;

        let dst_slice = buf.ok_or(RutabagaError::Unsupported)?;

        transfer_2d(
            info_2d.width,
            info_2d.height,
            transfer.x,
            transfer.y,
            transfer.w,
            transfer.h,
            transfer.stride,
            dst_offset,
            dst_slice,
            src_stride,
            src_offset,
            [VolatileSlice::new(info_2d.host_mem.as_mut_slice())]
                .iter()
                .cloned(),
        )?;

        resource.info_2d = Some(info_2d);
        Ok(())
    }
}
<commit_msg>rutabaga_gfx: collapse nested if in else block<commit_after>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! rutabaga_2d: Handles 2D virtio-gpu hypercalls.

use std::cmp::{max, min};

use data_model::*;

use crate::rutabaga_core::{Rutabaga2DInfo, RutabagaComponent, RutabagaResource};
use crate::rutabaga_utils::*;

/// Transfers a resource from potentially many chunked src VolatileSlices to a dst VolatileSlice.
pub fn transfer_2d<'a, S: Iterator<Item = VolatileSlice<'a>>>(
    resource_w: u32,
    resource_h: u32,
    rect_x: u32,
    rect_y: u32,
    rect_w: u32,
    rect_h: u32,
    dst_stride: u32,
    dst_offset: u64,
    dst: VolatileSlice,
    src_stride: u32,
    src_offset: u64,
    mut srcs: S,
) -> RutabagaResult<()> {
    if rect_w == 0 || rect_h == 0 {
        return Ok(());
    }

    checked_range!(checked_arithmetic!(rect_x + rect_w)?; <= resource_w)?;
    checked_range!(checked_arithmetic!(rect_y + rect_h)?; <= resource_h)?;

    let bytes_per_pixel = 4u64;

    let rect_x = rect_x as u64;
    let rect_y = rect_y as u64;
    let rect_w = rect_w as u64;
    let rect_h = rect_h as u64;

    let dst_stride = dst_stride as u64;
    let dst_offset = dst_offset as u64;
    let dst_resource_offset = dst_offset + (rect_y * dst_stride) + (rect_x * bytes_per_pixel);

    let src_stride = src_stride as u64;
    let src_offset = src_offset as u64;
    let src_resource_offset = src_offset + (rect_y * src_stride) + (rect_x * bytes_per_pixel);

    let mut next_src;
    let mut next_line;
    let mut current_height = 0u64;
    let mut src_opt = srcs.next();

    // Cumulative start offset of the current src.
    let mut src_start_offset = 0u64;
    while let Some(src) = src_opt {
        if current_height >= rect_h {
            break;
        }

        let src_size = src.size() as u64;

        // Cumulative end offset of the current src.
        let src_end_offset = checked_arithmetic!(src_start_offset + src_size)?;

        let src_line_vertical_offset = checked_arithmetic!(current_height * src_stride)?;
        let src_line_horizontal_offset = checked_arithmetic!(rect_w * bytes_per_pixel)?;

        // Cumulative start/end offsets of the next line to copy within all srcs.
        let src_line_start_offset =
            checked_arithmetic!(src_resource_offset + src_line_vertical_offset)?;
        let src_line_end_offset =
            checked_arithmetic!(src_line_start_offset + src_line_horizontal_offset)?;

        // Clamp the line start/end offset to be inside the current src.
        let src_copyable_start_offset = max(src_line_start_offset, src_start_offset);
        let src_copyable_end_offset = min(src_line_end_offset, src_end_offset);

        if src_copyable_start_offset < src_copyable_end_offset {
            let copyable_size =
                checked_arithmetic!(src_copyable_end_offset - src_copyable_start_offset)?;

            let offset_within_src = src_copyable_start_offset.saturating_sub(src_start_offset);

            if src_line_end_offset > src_end_offset {
                next_src = true;
                next_line = false;
            } else if src_line_end_offset == src_end_offset {
                next_src = true;
                next_line = true;
            } else {
                next_src = false;
                next_line = true;
            }

            let src_subslice = src.get_slice(offset_within_src as usize, copyable_size as usize)?;

            let dst_line_vertical_offset = checked_arithmetic!(current_height * dst_stride)?;
            let dst_line_horizontal_offset =
                checked_arithmetic!(src_copyable_start_offset - src_line_start_offset)?;
            let dst_line_offset =
                checked_arithmetic!(dst_line_vertical_offset + dst_line_horizontal_offset)?;
            let dst_start_offset = checked_arithmetic!(dst_resource_offset + dst_line_offset)?;

            let dst_subslice = dst.get_slice(dst_start_offset as usize, copyable_size as usize)?;

            src_subslice.copy_to_volatile_slice(dst_subslice);
        } else if src_line_start_offset >= src_start_offset {
            next_src = true;
            next_line = false;
        } else {
            next_src = false;
            next_line = true;
        };

        if next_src {
            src_start_offset = checked_arithmetic!(src_start_offset + src_size)?;
            src_opt = srcs.next();
        }

        if next_line {
            current_height += 1;
        }
    }

    Ok(())
}

pub struct Rutabaga2D {
    latest_created_fence_id: u32,
}

impl Rutabaga2D {
    pub fn init() -> RutabagaResult<Box<dyn RutabagaComponent>> {
        Ok(Box::new(Rutabaga2D {
            latest_created_fence_id: 0,
        }))
    }
}

impl RutabagaComponent for Rutabaga2D {
    fn create_fence(&mut self, fence_data: RutabagaFenceData) -> RutabagaResult<()> {
        self.latest_created_fence_id = fence_data.fence_id as u32;
        Ok(())
    }

    fn poll(&self) -> u32 {
        self.latest_created_fence_id
    }

    fn create_3d(
        &self,
        resource_id: u32,
        resource_create_3d: ResourceCreate3D,
    ) -> RutabagaResult<RutabagaResource> {
        // All virtio formats are 4 bytes per pixel.
        let resource_bpp = 4;
        let resource_stride = resource_bpp * resource_create_3d.width;
        let resource_size = (resource_stride as usize) * (resource_create_3d.height as usize);
        let info_2d = Rutabaga2DInfo {
            width: resource_create_3d.width,
            height: resource_create_3d.height,
            host_mem: vec![0; resource_size],
        };

        Ok(RutabagaResource {
            resource_id,
            handle: None,
            blob: false,
            blob_mem: 0,
            blob_flags: 0,
            map_info: None,
            info_2d: Some(info_2d),
            info_3d: None,
            vulkan_info: None,
            backing_iovecs: None,
        })
    }

    fn transfer_write(
        &self,
        _ctx_id: u32,
        resource: &mut RutabagaResource,
        transfer: Transfer3D,
    ) -> RutabagaResult<()> {
        if transfer.is_empty() {
            return Ok(());
        }

        let mut info_2d = resource.info_2d.take().ok_or(RutabagaError::Unsupported)?;

        let iovecs = resource
            .backing_iovecs
            .take()
            .ok_or(RutabagaError::Unsupported)?;

        // All offical virtio_gpu formats are 4 bytes per pixel.
        let resource_bpp = 4;
        let mut src_slices = Vec::with_capacity(iovecs.len());
        for iovec in &iovecs {
            // Safe because Rutabaga users should have already checked the iovecs.
            let slice = unsafe { VolatileSlice::from_raw_parts(iovec.base as *mut u8, iovec.len) };
            src_slices.push(slice);
        }

        let src_stride = resource_bpp * info_2d.width;
        let src_offset = transfer.offset;

        let dst_stride = resource_bpp * info_2d.width;
        let dst_offset = 0;

        transfer_2d(
            info_2d.width,
            info_2d.height,
            transfer.x,
            transfer.y,
            transfer.w,
            transfer.h,
            dst_stride,
            dst_offset,
            VolatileSlice::new(info_2d.host_mem.as_mut_slice()),
            src_stride,
            src_offset,
            src_slices.iter().cloned(),
        )?;

        resource.info_2d = Some(info_2d);
        resource.backing_iovecs = Some(iovecs);
        Ok(())
    }

    fn transfer_read(
        &self,
        _ctx_id: u32,
        resource: &mut RutabagaResource,
        transfer: Transfer3D,
        buf: Option<VolatileSlice>,
    ) -> RutabagaResult<()> {
        let mut info_2d = resource.info_2d.take().ok_or(RutabagaError::Unsupported)?;

        // All offical virtio_gpu formats are 4 bytes per pixel.
        let resource_bpp = 4;
        let src_stride = resource_bpp * info_2d.width;
        let src_offset = 0;
        let dst_offset = 0;

        let dst_slice = buf.ok_or(RutabagaError::Unsupported)?;

        transfer_2d(
            info_2d.width,
            info_2d.height,
            transfer.x,
            transfer.y,
            transfer.w,
            transfer.h,
            transfer.stride,
            dst_offset,
            dst_slice,
            src_stride,
            src_offset,
            [VolatileSlice::new(info_2d.host_mem.as_mut_slice())]
                .iter()
                .cloned(),
        )?;

        resource.info_2d = Some(info_2d);
        Ok(())
    }
}
<|endoftext|>


<commit_before>use stb_image::*;
use stb_image::bindgen::*;
use libc::types::os::arch::c95::c_int;
use ptr::{is_null, to_unsafe_ptr};
use str::as_c_str;
use vec::as_imm_buf;
use vec::raw::from_buf_raw;

pub struct Image<T> {
    width   : uint,
    height  : uint,
    depth   : uint,
    data    : ~[T],
}

pub fn new_image<T>(width: uint, height: uint, depth: uint, data: ~[T]) -> Image<T> {
    Image::<T> {
        width   : width,
        height  : height,
        depth   : depth,
        data    : move data,
    }
}

enum LoadResult {
    Error,
    ImageU8(Image<u8>),
    ImageF32(Image<f32>),
}

pub fn load(path: ~str) -> LoadResult {
    let force_depth = 0;
    load_with_depth(move path, force_depth, false)
}

priv fn load_internal<T>(buf : *T, w : c_int, h : c_int, d : c_int) -> Image<T> {
    unsafe {
        // FIXME: Shouldn't copy; instead we should use a sendable resource. They
        // aren't particularly safe yet though.
        let data = from_buf_raw(buf, (w * h * d) as uint);
        libc::free(buf as *c_void);
        Image::<T>{
            width   : w as uint,
            height  : h as uint,
            depth   : d as uint,
            data    : data}
    }
}

pub fn load_with_depth(path: ~str, force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width   = 0 as c_int;
            let mut height  = 0 as c_int;
            let mut depth   = 0 as c_int;
            as_c_str(path, |bytes| {
                if !convert_hdr && stbi_is_hdr(bytes)!=0   {
                    let buffer = stbi_loadf(
                        bytes, to_unsafe_ptr(&width), to_unsafe_ptr(&height),
                        to_unsafe_ptr(&depth), force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageF32( load_internal(buffer,width,height,depth) )
                    }
                } else {
                    let buffer = stbi_load(
                        bytes, to_unsafe_ptr(&width), to_unsafe_ptr(&height),
                        to_unsafe_ptr(&depth), force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageU8( load_internal(buffer,width,height,depth) )
                    }
                }
            })
        }
    }
}

pub fn load_from_memory(buffer: &[u8]) -> LoadResult {
    let force_depth = 0;
    load_from_memory_with_depth(buffer, force_depth, false)
}

pub fn load_from_memory_with_depth(buffer: &[u8], force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width = 0 as c_int;
            let mut height = 0 as c_int;
            let mut depth = 0 as c_int;
            as_imm_buf(buffer, |bytes, len| {
                if !convert_hdr && stbi_is_hdr_from_memory(bytes,len as c_int)!=0   {
                    let buffer = stbi_loadf_from_memory(
                        bytes, len as c_int, to_unsafe_ptr(&width),
                        to_unsafe_ptr(&height), to_unsafe_ptr(&depth),
                        force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        let actual_depth = if force_depth != 0 { force_depth as c_int } else { depth };
                        ImageF32( load_internal(buffer,width,height,actual_depth) )
                    }
                } else {
                    let buffer = stbi_load_from_memory(
                        bytes, len as c_int, to_unsafe_ptr(&width),
                        to_unsafe_ptr(&height), to_unsafe_ptr(&depth),
                        force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        let actual_depth = if force_depth != 0 { force_depth as c_int } else { depth };
                        ImageU8( load_internal(buffer,width,height,actual_depth) )
                    }
                }
            })
        }
    }
}
<commit_msg>Update for language changes<commit_after>use stb_image::*;
use stb_image::bindgen::*;
use libc::types::os::arch::c95::c_int;
use ptr::{is_null, to_unsafe_ptr};
use str::as_c_str;
use vec::as_imm_buf;
use vec::raw::from_buf_raw;

pub struct Image<T> {
    width   : uint,
    height  : uint,
    depth   : uint,
    data    : ~[T],
}

pub fn new_image<T>(width: uint, height: uint, depth: uint, data: ~[T]) -> Image<T> {
    Image::<T> {
        width   : width,
        height  : height,
        depth   : depth,
        data    : data,
    }
}

enum LoadResult {
    Error,
    ImageU8(Image<u8>),
    ImageF32(Image<f32>),
}

pub fn load(path: ~str) -> LoadResult {
    let force_depth = 0;
    load_with_depth(path, force_depth, false)
}

priv fn load_internal<T>(buf : *T, w : c_int, h : c_int, d : c_int) -> Image<T> {
    unsafe {
        // FIXME: Shouldn't copy; instead we should use a sendable resource. They
        // aren't particularly safe yet though.
        let data = from_buf_raw(buf, (w * h * d) as uint);
        libc::free(buf as *c_void);
        Image::<T>{
            width   : w as uint,
            height  : h as uint,
            depth   : d as uint,
            data    : data}
    }
}

pub fn load_with_depth(path: ~str, force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width   = 0 as c_int;
            let mut height  = 0 as c_int;
            let mut depth   = 0 as c_int;
            as_c_str(path, |bytes| {
                if !convert_hdr && stbi_is_hdr(bytes)!=0   {
                    let buffer = stbi_loadf(
                        bytes, to_unsafe_ptr(&width), to_unsafe_ptr(&height),
                        to_unsafe_ptr(&depth), force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageF32( load_internal(buffer,width,height,depth) )
                    }
                } else {
                    let buffer = stbi_load(
                        bytes, to_unsafe_ptr(&width), to_unsafe_ptr(&height),
                        to_unsafe_ptr(&depth), force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageU8( load_internal(buffer,width,height,depth) )
                    }
                }
            })
        }
    }
}

pub fn load_from_memory(buffer: &[u8]) -> LoadResult {
    let force_depth = 0;
    load_from_memory_with_depth(buffer, force_depth, false)
}

pub fn load_from_memory_with_depth(buffer: &[u8], force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width = 0 as c_int;
            let mut height = 0 as c_int;
            let mut depth = 0 as c_int;
            as_imm_buf(buffer, |bytes, len| {
                if !convert_hdr && stbi_is_hdr_from_memory(bytes,len as c_int)!=0   {
                    let buffer = stbi_loadf_from_memory(
                        bytes, len as c_int, to_unsafe_ptr(&width),
                        to_unsafe_ptr(&height), to_unsafe_ptr(&depth),
                        force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        let actual_depth = if force_depth != 0 { force_depth as c_int } else { depth };
                        ImageF32( load_internal(buffer,width,height,actual_depth) )
                    }
                } else {
                    let buffer = stbi_load_from_memory(
                        bytes, len as c_int, to_unsafe_ptr(&width),
                        to_unsafe_ptr(&height), to_unsafe_ptr(&depth),
                        force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        let actual_depth = if force_depth != 0 { force_depth as c_int } else { depth };
                        ImageU8( load_internal(buffer,width,height,actual_depth) )
                    }
                }
            })
        }
    }
}
<|endoftext|>


<commit_before>//! This module provides the backend API for the game logic

mod dictionary;
pub mod game;
mod image;
mod secret;
use crate::dictionary::ConfigParseError;
use crate::dictionary::Dict;
use crate::game::Game;
use crate::game::State;
use crate::image::Image;

pub const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
pub const AUTHOR: &str = "(c) Jens Getreu, 2016-2020.";

/// Title line.
pub const TITLE: &str = "ASCII-Hangman for Kids\n";

/// Number of wrong guess allowed.
pub const LIVES: u8 = 7;
/// Fallback sample configuration when no configuration file can be found.
pub const CONF_TEMPLATE: &str = "# Add own secrets here, one per line:\r
\r
secrets:\r
- guess me\r
- _good l_uck\r
- \"_der Hund:_| the dog\"\r
- _3*_7_=21_\r
\r
# Uncomment 3 lines to use a custom image:\r
\r
#image: |1\r
#   ::\r
# C|__|\r
";

/// State of the application.
#[derive(Debug)]
pub struct Backend {
    dict: Dict,
    game: Game,
    image: Image,
}

/// API to interact with all game logic. This is used by the desktop frontend
/// in `main.rs` or by the web-app frontend in `lib.rs`.
pub trait HangmanBackend {
    /// Initialize the application with config data and start the first game.
    fn new(config: &str) -> Result<Self, ConfigParseError>
    where
        Self: std::marker::Sized;

    /// The user_input is a key stroke. The meaning depends on the game's state:
    fn process_user_input(&mut self, inp: &str);

    /// Renders the image. Make sure it is up to date with `self.image.update()`.
    fn render_image(&self) -> String;

    /// Forward the private image dimension
    fn get_image_dimension(&self) -> (u8, u8);

    /// Renders the partly hidden secret.
    fn render_secret(&self) -> String;

    /// Informs about some game statistics: lifes
    fn render_game_lifes(&self) -> String;

    /// Informs about some game statistics: last guess
    fn render_game_last_guess(&self) -> String;

    /// Tells the user what to do next.
    fn render_instructions(&self) -> String;

    /// Forwards the game's state
    fn get_state(&self) -> State;
}

impl HangmanBackend for Backend {
    fn new(config: &str) -> Result<Self, ConfigParseError> {
        let mut dict = Dict::from(&config)?;
        // A dictionary guaranties to have least one secret.
        let secret = dict.get_random_secret().unwrap();
        let game = Game::new(&secret, LIVES, dict.is_empty());
        let mut image = Image::from(&config).or_else(|_| Image::new())?;
        image.update(&game);
        Ok(Self { dict, game, image })
    }

    fn process_user_input(&mut self, inp: &str) {
        match self.game.state {
            State::Victory => {
                // Start a new game. As we did not get a `State::VictoryGameOver` we know
                // there is at least one secret left.
                let secret = self.dict.get_random_secret().unwrap();
                self.game = Game::new(&secret, LIVES, self.dict.is_empty());
                self.image.update(&self.game);
            }

            State::VictoryGameOver => {}

            State::Defeat | State::DefeatGameOver => {
                // We will ask this secret again; this way we never end a game with a defeat.
                self.dict.add((self.game.secret).to_raw_string());
                // Start a new game. As we just added a secret, we know there is at least one.
                let secret = self.dict.get_random_secret().unwrap();
                self.game = Game::new(&secret, LIVES, self.dict.is_empty());
                self.image.update(&self.game);
            }
            State::Ongoing => {
                self.game.guess(inp.chars().next().unwrap_or(' '));
                // In case we lost, all secrets will be disclosed. Prevent disclosing the image in
                // that case.
                self.image.update(&self.game);
            }
        }
    }

    fn render_image(&self) -> String {
        format!("{}", self.image)
    }

    #[allow(dead_code)]
    fn get_image_dimension(&self) -> (u8, u8) {
        self.image.dimension
    }

    fn render_secret(&self) -> String {
        format!("{}", self.game.secret)
    }

    fn render_game_lifes(&self) -> String {
        format!("Lifes: {}", self.game.lifes)
    }

    fn render_game_last_guess(&self) -> String {
        format!("Last guess: {}", self.game.last_guess)
    }

    fn render_instructions(&self) -> String {
        match self.game.state {
            State::Victory => String::from("Congratulations! You won!"),
            State::VictoryGameOver => String::from("Congratulations! You won!"),
            State::Defeat | State::DefeatGameOver => String::from("You lost."),
            State::Ongoing => String::from("Type a letter, then press [Enter]:"),
        }
    }

    fn get_state(&self) -> State {
        self.game.state.clone()
    }
}
<commit_msg>update demo config<commit_after>//! This module provides the backend API for the game logic

mod dictionary;
pub mod game;
mod image;
mod secret;
use crate::dictionary::ConfigParseError;
use crate::dictionary::Dict;
use crate::game::Game;
use crate::game::State;
use crate::image::Image;

pub const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
pub const AUTHOR: &str = "(c) Jens Getreu, 2016-2020.";

/// Title line.
pub const TITLE: &str = "ASCII-Hangman for Kids\n";

/// Number of wrong guess allowed.
pub const LIVES: u8 = 7;
/// Fallback sample configuration when no configuration file can be found.
pub const CONF_TEMPLATE: &str = "# Add own secrets here, one per line:\r
\r
secrets:\r
- guess me\r
- _good l_uck\r
- \"_der Hund:_| the dog\"\r
- _3*_7_=21_\r
\r
\r
# Uncomment 3 lines to use a custom image:\r
\r
#image: |1\r
#   ::\r
# C|__|\r
";

/// State of the application.
#[derive(Debug)]
pub struct Backend {
    dict: Dict,
    game: Game,
    image: Image,
}

/// API to interact with all game logic. This is used by the desktop frontend
/// in `main.rs` or by the web-app frontend in `lib.rs`.
pub trait HangmanBackend {
    /// Initialize the application with config data and start the first game.
    fn new(config: &str) -> Result<Self, ConfigParseError>
    where
        Self: std::marker::Sized;

    /// The user_input is a key stroke. The meaning depends on the game's state:
    fn process_user_input(&mut self, inp: &str);

    /// Renders the image. Make sure it is up to date with `self.image.update()`.
    fn render_image(&self) -> String;

    /// Forward the private image dimension
    fn get_image_dimension(&self) -> (u8, u8);

    /// Renders the partly hidden secret.
    fn render_secret(&self) -> String;

    /// Informs about some game statistics: lifes
    fn render_game_lifes(&self) -> String;

    /// Informs about some game statistics: last guess
    fn render_game_last_guess(&self) -> String;

    /// Tells the user what to do next.
    fn render_instructions(&self) -> String;

    /// Forwards the game's state
    fn get_state(&self) -> State;
}

impl HangmanBackend for Backend {
    fn new(config: &str) -> Result<Self, ConfigParseError> {
        let mut dict = Dict::from(&config)?;
        // A dictionary guaranties to have least one secret.
        let secret = dict.get_random_secret().unwrap();
        let game = Game::new(&secret, LIVES, dict.is_empty());
        let mut image = Image::from(&config).or_else(|_| Image::new())?;
        image.update(&game);
        Ok(Self { dict, game, image })
    }

    fn process_user_input(&mut self, inp: &str) {
        match self.game.state {
            State::Victory => {
                // Start a new game. As we did not get a `State::VictoryGameOver` we know
                // there is at least one secret left.
                let secret = self.dict.get_random_secret().unwrap();
                self.game = Game::new(&secret, LIVES, self.dict.is_empty());
                self.image.update(&self.game);
            }

            State::VictoryGameOver => {}

            State::Defeat | State::DefeatGameOver => {
                // We will ask this secret again; this way we never end a game with a defeat.
                self.dict.add((self.game.secret).to_raw_string());
                // Start a new game. As we just added a secret, we know there is at least one.
                let secret = self.dict.get_random_secret().unwrap();
                self.game = Game::new(&secret, LIVES, self.dict.is_empty());
                self.image.update(&self.game);
            }
            State::Ongoing => {
                self.game.guess(inp.chars().next().unwrap_or(' '));
                // In case we lost, all secrets will be disclosed. Prevent disclosing the image in
                // that case.
                self.image.update(&self.game);
            }
        }
    }

    fn render_image(&self) -> String {
        format!("{}", self.image)
    }

    #[allow(dead_code)]
    fn get_image_dimension(&self) -> (u8, u8) {
        self.image.dimension
    }

    fn render_secret(&self) -> String {
        format!("{}", self.game.secret)
    }

    fn render_game_lifes(&self) -> String {
        format!("Lifes: {}", self.game.lifes)
    }

    fn render_game_last_guess(&self) -> String {
        format!("Last guess: {}", self.game.last_guess)
    }

    fn render_instructions(&self) -> String {
        match self.game.state {
            State::Victory => String::from("Congratulations! You won!"),
            State::VictoryGameOver => String::from("Congratulations! You won!"),
            State::Defeat | State::DefeatGameOver => String::from("You lost."),
            State::Ongoing => String::from("Type a letter, then press [Enter]:"),
        }
    }

    fn get_state(&self) -> State {
        self.game.state.clone()
    }
}
<|endoftext|>