<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Closure::*;
use self::Expression::*;
use std::collections::VecDeque;

#[derive(Debug, PartialEq, Clone)]
enum Closure {
    Depth(usize),
    TermInEnv(Term, Environment)
}

#[derive(Debug, PartialEq)]
enum Expression {
    Evaluation(Closure),
    Application(Box<Expression>, Term, Environment)
}

type Environment = VecDeque<Closure>;

fn whnf(term: Term, mut env: Environment) -> Expression {
    match term {
        Var(i) => match env.remove(i - 1) {
            Some(Depth(i)) => Evaluation(Depth(i)),
            Some(TermInEnv(t, e)) => whnf(t, e),
            None => unreachable!()
        },
        Abs(t) => Evaluation(TermInEnv(Abs(t), env)),
        App(lhs, rhs) => match whnf(*lhs, env.clone()) { // TODO: try to remove this clone()
            Evaluation(TermInEnv(Abs(lhs2), mut env2)) => {
                env2.push_front(TermInEnv(*rhs, env));
                whnf(*lhs2, env2)
            },
            expr => Application(Box::new(expr), *rhs, env)
        }
    }
}

fn reduce(depth: usize, exp: Expression) -> Term {
    match exp {
        Application(lhs, rhs, env) => app(reduce(depth, *lhs), nf(depth, rhs, env)),
        Evaluation(TermInEnv(Abs(term), mut env)) => {
            env.push_front(Depth(depth));
            abs(nf(depth + 1, *term, env))
        },
        Evaluation(TermInEnv(term, _)) => term,
        Evaluation(Depth(i)) => Var(depth - i)
    }
}

fn nf(depth: usize, term: Term, env: Environment) -> Term {
    reduce(depth, whnf(term, env))
}

/// Returns a term reduced to its normal form. Consumes its argument.
///
/// # Example
///
/// ```
/// use lambda_calculus::booleans::{if_else, tru};
/// use lambda_calculus::arithmetic::{zero, one, succ};
/// use lambda_calculus::reduction::normalize;
///
/// assert_eq!(normalize(succ().app(zero())), one());
/// assert_eq!(normalize(if_else().app(tru()).app(one()).app(zero())), one());
/// ```
pub fn normalize(term: Term) -> Term {
    nf(0, term, VecDeque::new())
}

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = true;

impl Term {
    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// // SUCC := λ λ λ 2 (3 2 1), ONE := λ λ 2 1
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(&*format!("{}", succ_one), "λλ2((λλ21)21)");
    /// ```
    pub fn beta_once(&mut self) {
        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_once(),
            App(_, _) => {
                let copy = self.clone();
                if let Ok(result) = copy.eval() {
                    if SHOW_REDUCTIONS { println!("    {} reduces to {}", self, result) }
                    *self = result
                } else if self.lhs_ref().unwrap().unvar_ref().is_err() {
                    self.lhs_ref_mut().unwrap().beta_once()
                } else {
                    self.rhs_ref_mut().unwrap().beta_once()
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// // PRED := λ λ λ 3 (λ λ 1 (2 4)) (λ 2) (λ 1), ONE := λ λ 2 1
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(&*format!("{}", pred_one), "λλ1");
    /// ```
    pub fn beta_full(&mut self) {
        if SHOW_REDUCTIONS { println!("reducing {}", self) }
        let mut tmp = self.clone();
        self.beta_once();

        while tmp != *self {
            tmp = self.clone();
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            self.beta_once();
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

#[cfg(test)]
mod test {
    use arithmetic::{succ, pred, is_zero, one};
    use term::*;
    use term::Term::*;
    use parser::parse;

    #[test]
    fn weak_head_normal_form() {
        // TODO
    }
/*
    #[test]
    fn beta_reduce_tmp() {
        let term = parse(&*"").unwrap();

        term.beta_full();

        assert_eq!(format!("{}", ret), "(λλ1(35))(λ2)")
    }
*/
    #[test]
    fn succ_beta_reduction() {
        //println!("({})({})", succ(), Term::from(1));
        let mut succ_one = succ().apply(1.into()).unwrap();
        succ_one.beta_full();

        assert_eq!(&*format!("{}", succ_one), "λλ2(21)")
    }
/*
    #[test]
    fn pred_beta_reduction() {
        //println!("({})({})", pred(), Term::from(1));
        let mut pred_one = pred().apply(1.into()).unwrap();
        pred_one.beta_full();

        assert_eq!(&*format!("{}", pred_one), "λλ1")
    }
*/
}
<commit_msg>Remove some tests in favour of doc tests<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Closure::*;
use self::Expression::*;
use std::collections::VecDeque;

#[derive(Debug, PartialEq, Clone)]
enum Closure {
    Depth(usize),
    TermInEnv(Term, Environment)
}

#[derive(Debug, PartialEq)]
enum Expression {
    Evaluation(Closure),
    Application(Box<Expression>, Term, Environment)
}

type Environment = VecDeque<Closure>;

fn whnf(term: Term, mut env: Environment) -> Expression {
    match term {
        Var(i) => match env.remove(i - 1) {
            Some(Depth(i)) => Evaluation(Depth(i)),
            Some(TermInEnv(t, e)) => whnf(t, e),
            None => unreachable!()
        },
        Abs(t) => Evaluation(TermInEnv(Abs(t), env)),
        App(lhs, rhs) => match whnf(*lhs, env.clone()) { // TODO: try to remove this clone()
            Evaluation(TermInEnv(Abs(lhs2), mut env2)) => {
                env2.push_front(TermInEnv(*rhs, env));
                whnf(*lhs2, env2)
            },
            expr => Application(Box::new(expr), *rhs, env)
        }
    }
}

fn reduce(depth: usize, exp: Expression) -> Term {
    match exp {
        Application(lhs, rhs, env) => app(reduce(depth, *lhs), nf(depth, rhs, env)),
        Evaluation(TermInEnv(Abs(term), mut env)) => {
            env.push_front(Depth(depth));
            abs(nf(depth + 1, *term, env))
        },
        Evaluation(TermInEnv(term, _)) => term,
        Evaluation(Depth(i)) => Var(depth - i)
    }
}

fn nf(depth: usize, term: Term, env: Environment) -> Term {
    reduce(depth, whnf(term, env))
}

/// Returns a term reduced to its normal form. Consumes its argument.
///
/// # Example
///
/// ```
/// use lambda_calculus::booleans::{if_else, tru};
/// use lambda_calculus::arithmetic::{zero, one, succ};
/// use lambda_calculus::reduction::normalize;
///
/// assert_eq!(normalize(succ().app(zero())), one());
/// assert_eq!(normalize(if_else().app(tru()).app(one()).app(zero())), one());
/// ```
pub fn normalize(term: Term) -> Term {
    nf(0, term, VecDeque::new())
}

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = true;

impl Term {
    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// // SUCC := λ λ λ 2 (3 2 1), ONE := λ λ 2 1
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(&*format!("{}", succ_one), "λλ2((λλ21)21)");
    /// ```
    pub fn beta_once(&mut self) {
        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_once(),
            App(_, _) => {
                let copy = self.clone();
                if let Ok(result) = copy.eval() {
                    if SHOW_REDUCTIONS { println!("    {} reduces to {}", self, result) }
                    *self = result
                } else if self.lhs_ref().unwrap().unvar_ref().is_err() {
                    self.lhs_ref_mut().unwrap().beta_once()
                } else {
                    self.rhs_ref_mut().unwrap().beta_once()
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// // PRED := λ λ λ 3 (λ λ 1 (2 4)) (λ 2) (λ 1), ONE := λ λ 2 1
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(&*format!("{}", pred_one), "λλ1");
    /// ```
    pub fn beta_full(&mut self) {
        if SHOW_REDUCTIONS { println!("reducing {}", self) }
        let mut tmp = self.clone();
        self.beta_once();

        while tmp != *self {
            tmp = self.clone();
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            self.beta_once();
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

#[cfg(test)]
mod test {

    #[test]
    fn weak_head_normal_form() {
        // TODO
    }
}
<|endoftext|>


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

//! retry
//!
//! this is an abstraction over the regular http get request. it allows you to
//! have a request retry until it succeeds, with a configurable number of
//! of attempts and a backoff strategy. It also takes care of automatically
//! deserializing responses and handles headers in a sane way.

use std::io::Read;
use std::time::Duration;
use std::thread;

use reqwest;
use reqwest::header;
use reqwest::header::ContentType;
use reqwest::{Method,Request};

use serde;
use serde_xml_rs;
use serde_json;

mod raw_deserializer;

use errors::*;

#[inline(always)]
pub fn default_initial_backoff() -> Duration { Duration::new(1,0) }
#[inline(always)]
pub fn default_max_backoff() -> Duration { Duration::new(5,0) }
#[inline(always)]
pub fn default_max_attempts() -> u32 { 0 }

pub trait Deserializer {
    fn deserialize<T, R>(&self, R) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>, R: Read;
    fn content_type(&self) -> ContentType;
}

#[derive(Debug, Clone, Copy)]
pub struct Xml;

impl Deserializer for Xml {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>, R: Read
    {
        serde_xml_rs::deserialize(r)
            .chain_err(|| format!("failed xml deserialization"))
    }
    fn content_type(&self) -> ContentType {
        ContentType("text/xml; charset=utf-8".parse().unwrap())
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Json;

impl Deserializer for Json {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
        where T: serde::de::DeserializeOwned, R: Read
    {
        serde_json::from_reader(r)
            .chain_err(|| format!("failed json deserialization"))
    }
    fn content_type(&self) -> ContentType {
        ContentType("text/json; charset=utf-8".parse().unwrap())
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Raw;

impl Deserializer for Raw {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>, R: Read
    {
        raw_deserializer::from_reader(r)
            .chain_err(|| format!("failed raw deserialization"))
    }
    fn content_type(&self) -> ContentType {
        ContentType("text/plain; charset=utf-8".parse().unwrap())
    }
}

#[derive(Debug, Clone)]
pub struct Client {
    client: reqwest::Client,
    headers: header::Headers,
    initial_backoff: Duration,
    max_backoff: Duration,
    max_attempts: u32,
    return_on_404: bool,
}

impl Client {
    pub fn new() -> Result<Self> {
        let client = reqwest::Client::new()
            .chain_err(|| format!("failed to initialize client"))?;
        Ok(Client{
            client,
            headers: header::Headers::new(),
            initial_backoff: default_initial_backoff(),
            max_backoff:  default_max_backoff(),
            max_attempts: default_max_attempts(),
            return_on_404: false,
        })
    }

    pub fn header<H>(mut self, h: H) -> Self
        where H: header::Header
    {
        self.headers.set(h);
        self
    }

    pub fn initial_backoff(mut self, initial_backoff: Duration) -> Self {
        self.initial_backoff = initial_backoff;
        self
    }

    pub fn max_backoff(mut self, max_backoff: Duration) -> Self {
        self.max_backoff = max_backoff;
        self
    }

    /// max_attempts will panic if the argument is greater than 500
    pub fn max_attempts(mut self, max_attempts: u32) -> Self {
        if max_attempts > 500 {
            // Picking 500 as a max_attempts number arbitrarily, to prevent the mutually recursive
            // functions dispatch_request and wait_then_retry from blowing up the stack
            panic!("max_attempts cannot be greater than 500")
        } else {
            self.max_attempts = max_attempts;
            self
        }
    }

    pub fn return_on_404(mut self, return_on_404: bool) -> Self {
        self.return_on_404 = return_on_404;
        self
    }

    pub fn get<D>(&self, d: D, url: String) -> RequestBuilder<D>
        where D: Deserializer
    {
        RequestBuilder{
            url,
            d,
            client: self.client.clone(),
            headers: self.headers.clone(),
            initial_backoff: self.initial_backoff.clone(),
            max_backoff: self.max_backoff.clone(),
            max_attempts: self.max_attempts.clone(),
            return_on_404: self.return_on_404.clone(),
        }
    }
}

pub struct RequestBuilder<D>
    where D: Deserializer
{
    url: String,
    d: D,
    client: reqwest::Client,
    headers: header::Headers,
    initial_backoff: Duration,
    max_backoff: Duration,
    max_attempts: u32,
    return_on_404: bool,
}

impl<D> RequestBuilder<D>
    where D: Deserializer
{

    pub fn header<H>(mut self, h: H) -> Self
        where H: header::Header
    {
        self.headers.set(h);
        self
    }

    pub fn send<T>(&self) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>
    {
        let url = reqwest::Url::parse(self.url.as_str())
            .chain_err(|| format!("failed to parse uri"))?;
        let mut req = Request::new(Method::Get, url);
        req.headers_mut().extend(self.headers.iter());
        req.headers_mut().set(self.d.content_type());
        self.dispatch_request(req, self.initial_backoff, 0)
    }

    fn dispatch_request<T>(&self, req: Request, delay: Duration, num_attempts: u32) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>
    {
        info!("Fetching {}: Attempt#{}", req.url(), num_attempts + 1);

        match self.client.execute(clone_request(&req)) {
            Ok(resp) => {
                match (resp.status(), self.return_on_404) {
                    (reqwest::StatusCode::Ok,_) => self.d.deserialize(resp)
                        .chain_err(|| format!("failed to deserialize data")),
                    (reqwest::StatusCode::NotFound,true) => {
                        // TODO: return empty (failed?)
                        error!("Failed to fetch: should return!");
                        self.wait_then_retry(req, delay, num_attempts)
                    }
                    (s,_) => {
                        info!("Failed to fetch: {}", s);
                        self.wait_then_retry(req, delay, num_attempts)
                    }
                }
            }
            Err(e) => {
                info!("Failed to fetch: {}", e);
                self.wait_then_retry(req, delay, num_attempts)
            }
        }
    }

    fn wait_then_retry<T>(&self, req: Request, delay: Duration, num_attempts: u32) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>
    {
        thread::sleep(delay);
        let delay = if self.max_backoff != Duration::new(0,0) && delay * 2 > self.max_backoff {
                self.max_backoff
            } else {
                delay * 2
            };
        let num_attempts = num_attempts + 1;
        if self.max_attempts != 0 && num_attempts == self.max_attempts {
            Err(format!("Timed out while fetching {}", req.url()).into())
        } else {
            self.dispatch_request(req, delay, num_attempts)
        }
    }
}

fn clone_request(req: &Request) -> Request {
    let mut newreq = Request::new(req.method().clone(), req.url().clone());
    newreq.headers_mut().extend(req.headers().iter());
    newreq
}
<commit_msg>retry: fixed issue where retry recursed forever<commit_after>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! retry
//!
//! this is an abstraction over the regular http get request. it allows you to
//! have a request retry until it succeeds, with a configurable number of
//! of attempts and a backoff strategy. It also takes care of automatically
//! deserializing responses and handles headers in a sane way.

use std::io::Read;
use std::time::Duration;
use std::thread;

use reqwest;
use reqwest::header;
use reqwest::header::ContentType;
use reqwest::{Method,Request};

use serde;
use serde_xml_rs;
use serde_json;

mod raw_deserializer;

use errors::*;

#[inline(always)]
pub fn default_initial_backoff() -> Duration { Duration::new(1,0) }
#[inline(always)]
pub fn default_max_backoff() -> Duration { Duration::new(5,0) }
#[inline(always)]
pub fn default_max_attempts() -> u32 { 10 }

pub trait Deserializer {
    fn deserialize<T, R>(&self, R) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>, R: Read;
    fn content_type(&self) -> ContentType;
}

#[derive(Debug, Clone, Copy)]
pub struct Xml;

impl Deserializer for Xml {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>, R: Read
    {
        serde_xml_rs::deserialize(r)
            .chain_err(|| format!("failed xml deserialization"))
    }
    fn content_type(&self) -> ContentType {
        ContentType("text/xml; charset=utf-8".parse().unwrap())
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Json;

impl Deserializer for Json {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
        where T: serde::de::DeserializeOwned, R: Read
    {
        serde_json::from_reader(r)
            .chain_err(|| format!("failed json deserialization"))
    }
    fn content_type(&self) -> ContentType {
        ContentType("text/json; charset=utf-8".parse().unwrap())
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Raw;

impl Deserializer for Raw {
    fn deserialize<T, R>(&self, r: R) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>, R: Read
    {
        raw_deserializer::from_reader(r)
            .chain_err(|| format!("failed raw deserialization"))
    }
    fn content_type(&self) -> ContentType {
        ContentType("text/plain; charset=utf-8".parse().unwrap())
    }
}

#[derive(Debug, Clone)]
pub struct Client {
    client: reqwest::Client,
    headers: header::Headers,
    initial_backoff: Duration,
    max_backoff: Duration,
    max_attempts: u32,
    return_on_404: bool,
}

impl Client {
    pub fn new() -> Result<Self> {
        let client = reqwest::Client::new()
            .chain_err(|| format!("failed to initialize client"))?;
        Ok(Client{
            client,
            headers: header::Headers::new(),
            initial_backoff: default_initial_backoff(),
            max_backoff:  default_max_backoff(),
            max_attempts: default_max_attempts(),
            return_on_404: false,
        })
    }

    pub fn header<H>(mut self, h: H) -> Self
        where H: header::Header
    {
        self.headers.set(h);
        self
    }

    pub fn initial_backoff(mut self, initial_backoff: Duration) -> Self {
        self.initial_backoff = initial_backoff;
        self
    }

    pub fn max_backoff(mut self, max_backoff: Duration) -> Self {
        self.max_backoff = max_backoff;
        self
    }

    /// max_attempts will panic if the argument is greater than 500
    pub fn max_attempts(mut self, max_attempts: u32) -> Self {
        if max_attempts > 500 {
            // Picking 500 as a max_attempts number arbitrarily, to prevent the mutually recursive
            // functions dispatch_request and wait_then_retry from blowing up the stack
            panic!("max_attempts cannot be greater than 500")
        } else {
            self.max_attempts = max_attempts;
            self
        }
    }

    pub fn return_on_404(mut self, return_on_404: bool) -> Self {
        self.return_on_404 = return_on_404;
        self
    }

    pub fn get<D>(&self, d: D, url: String) -> RequestBuilder<D>
        where D: Deserializer
    {
        RequestBuilder{
            url,
            d,
            client: self.client.clone(),
            headers: self.headers.clone(),
            initial_backoff: self.initial_backoff.clone(),
            max_backoff: self.max_backoff.clone(),
            max_attempts: self.max_attempts.clone(),
            return_on_404: self.return_on_404.clone(),
        }
    }
}

pub struct RequestBuilder<D>
    where D: Deserializer
{
    url: String,
    d: D,
    client: reqwest::Client,
    headers: header::Headers,
    initial_backoff: Duration,
    max_backoff: Duration,
    max_attempts: u32,
    return_on_404: bool,
}

impl<D> RequestBuilder<D>
    where D: Deserializer
{

    pub fn header<H>(mut self, h: H) -> Self
        where H: header::Header
    {
        self.headers.set(h);
        self
    }

    pub fn send<T>(&self) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>
    {
        let url = reqwest::Url::parse(self.url.as_str())
            .chain_err(|| format!("failed to parse uri"))?;
        let mut req = Request::new(Method::Get, url);
        req.headers_mut().extend(self.headers.iter());
        req.headers_mut().set(self.d.content_type());
        self.dispatch_request(req, self.initial_backoff, 0)
    }

    fn dispatch_request<T>(&self, req: Request, delay: Duration, num_attempts: u32) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>
    {
        info!("Fetching {}: Attempt#{}", req.url(), num_attempts + 1);

        match self.client.execute(clone_request(&req)) {
            Ok(resp) => {
                match (resp.status(), self.return_on_404) {
                    (reqwest::StatusCode::Ok,_) => self.d.deserialize(resp)
                        .chain_err(|| format!("failed to deserialize data")),
                    (reqwest::StatusCode::NotFound,true) => {
                        // TODO: return empty (failed?)
                        error!("Failed to fetch: should return!");
                        self.wait_then_retry(req, delay, num_attempts)
                    }
                    (s,_) => {
                        info!("Failed to fetch: {}", s);
                        self.wait_then_retry(req, delay, num_attempts)
                    }
                }
            }
            Err(e) => {
                info!("Failed to fetch: {}", e);
                self.wait_then_retry(req, delay, num_attempts)
            }
        }
    }

    fn wait_then_retry<T>(&self, req: Request, delay: Duration, num_attempts: u32) -> Result<T>
        where T: for<'de> serde::Deserialize<'de>
    {
        thread::sleep(delay);
        let delay = if self.max_backoff != Duration::new(0,0) && delay * 2 > self.max_backoff {
                self.max_backoff
            } else {
                delay * 2
            };
        let num_attempts = num_attempts + 1;
        if num_attempts == self.max_attempts {
            Err(format!("Timed out while fetching {}", req.url()))
        } else {
            self.dispatch_request(req, delay, num_attempts)
        }
    }
}

fn clone_request(req: &Request) -> Request {
    let mut newreq = Request::new(req.method().clone(), req.url().clone());
    newreq.headers_mut().extend(req.headers().iter());
    newreq
}
<|endoftext|>


<commit_before>use std::sync::mpsc::Sender;
use ::kuchiki::traits::*;

use status::*;
use state_manager::*;
use resources::*;

pub struct Responser {}

impl Responser {

    pub fn new () -> Self { Responser {} }
    pub fn try_recv (&self, mut app: &mut ::App) {
        match app.rx_res.try_recv() {
            Ok(item) => {
                match item.extra {
                    Some(o) => {
                        match o {
                            ChannelItemType::Show(extra) => {
                                let document = ::kuchiki::parse_html().from_utf8().one(item.result.as_bytes());

                                let posturl = get_posturl(&extra.postid, extra.page);

                                app.status_bar.append(&app.screen_manager,
                                                      &format!("[{}-{}:ROK][{}]",
                                                               app.show_item.url_query.message,
                                                               app.show_item.page,
                                                               app.state_manager.is_web_request()));

                                match app.show_builder.build(&document, &posturl) {
                                    Ok(item) => {
                                        app.show_item = item;

                                        // get all images links in an array, and send to background download
                                        let maps = app.show_item.replies.iter().flat_map(|reply| {
                                                let f = reply.body.iter().filter(|node| {
                                                        let node2 = node.clone();
                                                        match *node2 {
                                                            ::reply_model::NodeType::Image(ref n) => {
                                                                (n.data.starts_with("http") || n.data.starts_with("https")) && n.alt.starts_with("[img]") && n.alt.ends_with("[/img]")
                                                            }
                                                            _ => false,
                                                        }
                                                    }).collect::<Vec<_>>();
                                                f
                                            })
                                            .collect::<Vec<_>>();

                                        let mut count = app.image_request_count_lock.lock().expect("fail to lock image request count");
                                        *count += maps.len();
                                        // let count = maps.len();
                                        app.state_manager.set_bg_request(true);
                                        app.status_bar.append(&app.screen_manager,
                                                              &format!("[SIMG:{count}]", count = *count));

                                        for node in &maps {
                                            let node2 = node.clone();
                                            match *node2 {
                                                ::reply_model::NodeType::Image(ref n) => {
                                                    let status_message = image_request(&n.data, &mut app.state_manager, &app.tx_req);
                                                    app.status_bar.append(&app.screen_manager, &status_message);
                                                }
                                                _ => {}
                                            }
                                        }

                                        app.show.reset_y();
                                        ::screen::common::clear_screen();
                                        app.state_manager.update_state(Status::Show); //state = Status::Show;
                                    },
                                    Err(e) => app.status_bar.append(&app.screen_manager, &"[SPFAIL]")
                                };

                                app.state_manager.set_web_request(false); // is_web_requesting = false;
                            }
                            ChannelItemType::Index(_) => {
                                let document = ::kuchiki::parse_html().from_utf8().one(item.result.as_bytes());

                                app.list_topic_items.clear();

                                match app.index_builder.build(&document) {
                                    Ok(items) => {
                                        for item in items {
                                            app.list_topic_items.push(item);
                                        }

                                        app.status_bar.append(&app.screen_manager, &format!("[TOPICS:ROK]"));

                                        ::screen::common::clear_screen();

                                        app.state_manager.update_state(Status::List); // state = Status::List;
                                        app.state_manager.set_web_request(false); // is_web_requesting = false;
                                    },
                                    Err(e) => app.status_bar.append(&app.screen_manager, &"[IPFAIL]")
                                }

                            }
                            ChannelItemType::Image(extra) => {

                                match app.image_request_count_lock.lock() {
                                    Ok(mut count) => {
                                        if *count == 0 {
                                            app.status_bar.append(&app.screen_manager, &format!("[RIMG:CERR]"));
                                        } else {
                                            *count -= 1;
                                            if item.result != "" {
                                                app.status_bar.append(&app.screen_manager,
                                                                      &format!("[RIMG:E-{count}-{error}]",
                                                                               count = *count,
                                                                               error = item.result));
                                            } else {
                                                app.status_bar.append(&app.screen_manager,
                                                                      &format!("[RIMG:S-{count}]", count = *count));
                                            }
                                        }

                                        ::screen::common::clear_screen();

                                        if *count <= 0 {
                                            app.state_manager.set_bg_request(false);
                                            ::screen::common::clear_screen();
                                            app.state_manager.set_web_request(false); // is_web_requesting = false;
                                        }
                                    }
                                    Err(poisoned) => {
                                        app.status_bar.append(&app.screen_manager, &format!("[IMAGES:LOCKERR]"));
                                    }
                                };
                            }
                        }
                    }
                    None => { }
                }
            }
            Err(_) => {}
        }
    }

}

fn get_posturl(postid: &String, page: usize) -> String {
    let base_url = "http://forum1.hkgolden.com/view.aspx";
    let posturl = format!("{base_url}?type=BW&message={postid}&page={page}",
                          base_url = base_url,
                          postid = postid,
                          page = page);
    posturl
}

fn image_request(url: &String, state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let ci = ChannelItem {
        extra: Some(ChannelItemType::Image(ChannelImageItem {
                                  url: url.to_string(),
                                  bytes: Default::default(),
                              })),
        result: Default::default(),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            state_manager.set_web_request(true); // *is_web_requesting = true;
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}
<commit_msg>remove lock in image responser<commit_after>use std::sync::mpsc::Sender;
use ::kuchiki::traits::*;

use status::*;
use state_manager::*;
use resources::*;

pub struct Responser {}

impl Responser {

    pub fn new () -> Self { Responser {} }
    pub fn try_recv (&self, mut app: &mut ::App) {
        match app.rx_res.try_recv() {
            Ok(item) => {
                info!("respsoner receive item");
                match item.extra {
                    Some(o) => {
                        match o {
                            ChannelItemType::Show(extra) => {
                                let document = ::kuchiki::parse_html().from_utf8().one(item.result.as_bytes());

                                let posturl = get_posturl(&extra.postid, extra.page);

                                app.status_bar.append(&app.screen_manager,
                                                      &format!("[{}-{}:ROK][{}]",
                                                               app.show_item.url_query.message,
                                                               app.show_item.page,
                                                               app.state_manager.is_web_request()));

                                match app.show_builder.build(&document, &posturl) {
                                    Ok(item) => {
                                        app.show_item = item;

                                        // get all images links in an array, and send to background download
                                        let maps = app.show_item.replies.iter().flat_map(|reply| {
                                                let f = reply.body.iter().filter(|node| {
                                                        let node2 = node.clone();
                                                        match *node2 {
                                                            ::reply_model::NodeType::Image(ref n) => {
                                                                (n.data.starts_with("http") || n.data.starts_with("https")) && n.alt.starts_with("[img]") && n.alt.ends_with("[/img]")
                                                            }
                                                            _ => false,
                                                        }
                                                    }).collect::<Vec<_>>();
                                                f
                                            })
                                            .collect::<Vec<_>>();

                                        let count = maps.len();
                                        app.status_bar.append(&app.screen_manager,
                                                              &format!("[SIMG:{count}]", count = count));

                                        for node in &maps {
                                            let node2 = node.clone();
                                            match *node2 {
                                                ::reply_model::NodeType::Image(ref n) => {
                                                    let status_message = image_request(&n.data, &mut app.state_manager, &app.tx_req);
                                                    app.status_bar.append(&app.screen_manager, &status_message);
                                                }
                                                _ => {}
                                            }
                                        }

                                        app.show.reset_y();
                                        ::screen::common::clear_screen();
                                        app.state_manager.update_state(Status::Show); //state = Status::Show;
                                    },
                                    Err(e) => app.status_bar.append(&app.screen_manager, &"[SPFAIL]")
                                };

                                app.state_manager.set_web_request(false); // is_web_requesting = false;
                            }
                            ChannelItemType::Index(_) => {
                                let document = ::kuchiki::parse_html().from_utf8().one(item.result.as_bytes());

                                app.list_topic_items.clear();

                                match app.index_builder.build(&document) {
                                    Ok(items) => {
                                        for item in items {
                                            app.list_topic_items.push(item);
                                        }

                                        app.status_bar.append(&app.screen_manager, &format!("[TOPICS:ROK]"));

                                        ::screen::common::clear_screen();

                                        app.state_manager.update_state(Status::List); // state = Status::List;
                                        app.state_manager.set_web_request(false); // is_web_requesting = false;
                                    },
                                    Err(e) => app.status_bar.append(&app.screen_manager, &"[IPFAIL]")
                                }

                            }
                            ChannelItemType::Image(extra) => {

                                if item.result != "" {
                                    app.status_bar.append(&app.screen_manager,
                                                          &format!("[RIMG:E-{error}]", error = item.result));
                                } else {
                                    app.status_bar.append(&app.screen_manager, "[RIMG:S]");
                                }
                            }
                        }
                    }
                    None => { }
                }
            }
            Err(_) => {}
        }
    }

}

fn get_posturl(postid: &String, page: usize) -> String {
    let base_url = "http://forum1.hkgolden.com/view.aspx";
    let posturl = format!("{base_url}?type=BW&message={postid}&page={page}",
                          base_url = base_url,
                          postid = postid,
                          page = page);
    posturl
}

fn image_request(url: &String, state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let url2 = url.clone();

    info!("image_request - url: {}", url2);
    let ci = ChannelItem {
        extra: Some(ChannelItemType::Image(ChannelImageItem {
                                  url: url.to_string(),
                                  bytes: Default::default(),
                              })),
        result: Default::default(),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}
<|endoftext|>


<commit_before>//! Initialization of the signed distance function near the level set.
use super::min;
use std;

/// Computes the signed distance function from a plane given as the _zero_ level set of a
/// linear function on a tetrahedron at 4 points with unit coordinates starting at (0, 0, 0) and
/// ending at (1, 1, 1), and in between exactly one coordinate changes from 0 to 1.
///
/// Inputs are `u`, the values at the vertices.
///
/// The function returns the values of the (non-signed) distance function or `None` if the zero
/// level set does not pass through the tetrahedron.
pub fn tetrahedron_dist<F>(mut u: [f64; 4], mut dual_norm: F, perm: [usize; 3]) -> Option<[f64; 4]>
where
    F: FnMut([f64; 3]) -> f64,
{
    let mut n_pos = 0;
    let mut n_neg = 0;
    for u in &mut u {
        if *u > 0. {
            n_pos += 1;
        } else if *u < 0. {
            n_neg += 1;
        }
    }
    // check if sign differs (level set goes throught the triangle)
    if n_neg == 4 || n_pos == 4 {
        return None;
    }

    // everything is zero
    if n_neg + n_pos == 0 {
        return Some([0.; 4]);
    }

    let g = [u[1] - u[0], u[2] - u[1], u[3] - u[2]];
    let g = [g[perm[0]], g[perm[1]], g[perm[2]]];
    let g_norm_rcp = 1. / dual_norm(g);

    // FIXME: this requires for norm to be even
    // Support triangular anisotropies?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// Initializes the distance around the free boundary.
///
/// Based on the level set function with values `u` given on a regular grid, it computes the
/// distance from the _zero_ level set in the nodes of the triangles through which the level set
/// passes.  Stores the result in the preallocated slice `d`.
///
/// Nodes away from the boundary have their value set to `std::f64::MAX`.
///
/// Splits every cube into six tetrahedra and computes the distance on each of them.
pub fn init_dist_3d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 3]) -> f64,
{
    let (nx, ny, nz) = dim;
    assert_eq!(nx * ny * nz, u.len());
    assert_eq!(nx * ny * nz, d.len());

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    // split each cube into 6 tetrahedrons
    let ids = [
        [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)],
    ];

    let perms = [
        [0, 1, 2],
        [0, 2, 1],
        [1, 0, 2],
        [2, 0, 1],
        [1, 2, 0],
        [2, 1, 0],
    ];

    for i in 1..nx {
        for j in 1..ny {
            for k in 1..nz {
                let s = i * ny * nz + j * nz + k;
                let mut v = [0.; 4];

                for (idx, perm) in ids.iter().zip(perms.iter()) {
                    for m in 0..4 {
                        v[m] = u[s - idx[m].0 * ny * nz - idx[m].1 * nz - idx[m].2];
                    }

                    let r = tetrahedron_dist(v, &mut dual_norm, *perm);
                    if let Some(r) = r {
                        for m in 0..4 {
                            let q = s - idx[m].0 * ny * nz - idx[m].1 * nz - idx[m].2;
                            d[q] = min(d[q], r[m]);
                        }
                    }
                }
            }
        }
    }
}

/// Compute the anisotropic distance to the zero level set of a function on a axes-aligned
/// right triangle. The right angle is assumed to be at vertex 1.
///
/// `perm` specifies which leg of the triangle corresponds to the directions e₁ and e₂:
///
/// `[0, 1]`: 0-1 leg is parallel to e₁, 1-2 leg is parallel to e₂
/// `[1, 0]`: 0-1 leg is parallel to e₂, 1-2 leg is parallel to e₁
///
/// ```text
/// [0, 1]    [1, 0]
/// ------    ------
///
///     2      1--2
///    /|      | /
///   / |      |/
///  0--1      0
/// ```
///
fn triangle_dist<F>(mut u: [f64; 3], perm: [usize; 2], mut dual_norm: F) -> Option<[f64; 3]>
where
    F: FnMut([f64; 2]) -> f64,
{
    // check if sign differs (level set goes throught the triangle)
    if (u[0] > 0. && u[1] > 0. && u[2] > 0.) || (u[0] < 0. && u[1] < 0. && u[2] < 0.) {
        return None;
    }

    let g = [u[1] - u[0], u[2] - u[1]];
    let g = [g[perm[0]], g[perm[1]]];
    let norm = dual_norm(g);
    // all values are zero
    if norm == 0. {
        return Some([0., 0., 0.]);
    }
    let g_norm_rcp = 1. / norm;

    // TODO: This supports only even norms. Do we want to support triangle norms, for instance?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// As `init_dist_2d`, but for general anisotropic norm.
///
/// `dual_norm` is the __dual__ norm. It must be a positively one-homogeneous function.
pub fn init_dist_2d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 2]) -> f64,
{
    let (nx, ny) = dim;
    assert_eq!(nx * ny, u.len());
    assert_eq!(nx * ny, d.len());

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    for j in 1..nx {
        for i in 1..ny {
            let s = j * ny + i;
            let v = [s - ny - 1, s - ny, s];
            let r = triangle_dist([u[v[0]], u[v[1]], u[v[2]]], [1, 0], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
            let v = [s - ny - 1, s - 1, s];
            let r = triangle_dist([u[v[0]], u[v[1]], u[v[2]]], [0, 1], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::norm::{DualNorm, EuclideanNorm};

    #[test]
    fn simple_triangles() {
        let eucl_triangle_dist = |v| triangle_dist(v, [0, 1], |p| EuclideanNorm.dual_norm(p));
        assert_eq!(eucl_triangle_dist([0., 0., 0.]), Some([0., 0., 0.]));
        assert_eq!(eucl_triangle_dist([1., 1., 1.]), None);
        assert_eq!(eucl_triangle_dist([-1., -1., -1.]), None);
        assert_eq!(
            eucl_triangle_dist([0., 1., 0.]),
            Some([0., 1. / (2f64).sqrt(), 0.])
        );
        assert_eq!(eucl_triangle_dist([0., -1., -1.]), Some([0., 1., 1.]));
        assert_eq!(eucl_triangle_dist([0., 1., 1.]), Some([0., 1., 1.]));
        assert_eq!(eucl_triangle_dist([1., 1., 0.]), Some([1., 1., 0.]));
        assert_eq!(eucl_triangle_dist([-1., 0., 0.]), Some([1., 0., 0.]));
        assert_eq!(
            eucl_triangle_dist([1., 0., 1.]),
            Some([1. / (2f64).sqrt(), 0., 1. / (2f64).sqrt()])
        );
    }

    #[test]
    fn anisotropic_norm_2d() {
        // Du = (1, 0)
        let u = [0., 0., 1., 1.];
        let mut d = [0.; 4];
        init_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0., 1., 1.]);

        // Du = (0, 1)
        let u = [0., 1., 0., 1.];
        let mut d = [0.; 4];
        init_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0.5, 0., 0.5]);
    }

    #[test]
    fn anisotropic_norm_3d() {
        // Du = (1, 0, 0)
        let u = [0., 0., 0., 0., 1., 1., 1., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0., 0., 1., 1., 1., 1.]);

        // Du = (0, 1, 0)
        let u = [0., 0., 1., 1., 0., 0., 1., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0.5, 0.5, 0., 0., 0.5, 0.5]);

        // Du = (0, 0, 1)
        let u = [0., 1., 0., 1., 0., 1., 0., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0.25, 0., 0.25, 0., 0.25, 0., 0.25]);
    }
}
<commit_msg>Precheck cube in init_dist_3d<commit_after>//! Initialization of the signed distance function near the level set.
use super::min;
use std;

/// Computes the signed distance function from a plane given as the _zero_ level set of a
/// linear function on a tetrahedron at 4 points with unit coordinates starting at (0, 0, 0) and
/// ending at (1, 1, 1), and in between exactly one coordinate changes from 0 to 1.
///
/// Inputs are `u`, the values at the vertices.
///
/// The function returns the values of the (non-signed) distance function or `None` if the zero
/// level set does not pass through the tetrahedron.
pub fn tetrahedron_dist<F>(mut u: [f64; 4], mut dual_norm: F, perm: [usize; 3]) -> Option<[f64; 4]>
where
    F: FnMut([f64; 3]) -> f64,
{
    let mut n_pos = 0;
    let mut n_neg = 0;
    for u in &mut u {
        if *u > 0. {
            n_pos += 1;
        } else if *u < 0. {
            n_neg += 1;
        }
    }
    // check if sign differs (level set goes throught the triangle)
    if n_neg == 4 || n_pos == 4 {
        return None;
    }

    // everything is zero
    if n_neg + n_pos == 0 {
        return Some([0.; 4]);
    }

    let g = [u[1] - u[0], u[2] - u[1], u[3] - u[2]];
    let g = [g[perm[0]], g[perm[1]], g[perm[2]]];
    let g_norm_rcp = 1. / dual_norm(g);

    // FIXME: this requires for norm to be even
    // Support triangular anisotropies?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// Initializes the distance around the free boundary.
///
/// Based on the level set function with values `u` given on a regular grid, it computes the
/// distance from the _zero_ level set in the nodes of the triangles through which the level set
/// passes.  Stores the result in the preallocated slice `d`.
///
/// Nodes away from the boundary have their value set to `std::f64::MAX`.
///
/// Splits every cube into six tetrahedra and computes the distance on each of them.
pub fn init_dist_3d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 3]) -> f64,
{
    let (ni, nj, nk) = dim;
    assert_eq!(ni * nj * nk, u.len());
    assert_eq!(ni * nj * nk, d.len());
    let (si, sj, _sk) = (nj * nk, nk, 1);

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    // split each cube into 6 tetrahedrons
    let ids = [
        [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)],
    ];

    let perms = [
        [0, 1, 2],
        [0, 2, 1],
        [1, 0, 2],
        [2, 0, 1],
        [1, 2, 0],
        [2, 1, 0],
    ];

    for i in 1..ni {
        for j in 1..nj {
            let s = i * si + j * sj;
            let v = [u[s], u[s - si], u[s - sj], u[s - si - sj]];
            let mut all_pos_prev = v.iter().all(|&v| v > 0.);
            let mut all_neg_prev = v[0] < 0. && v[1] < 0. && v[2] < 0. && v[3] < 0.;
            for k in 1..nk {
                let s = i * si + j * sj + k;
                let v = [u[s], u[s - si], u[s - sj], u[s - si - sj]];
                let mut all_pos = v.iter().all(|&v| v > 0.);
                let mut all_neg = v[0] < 0. && v[1] < 0. && v[2] < 0. && v[3] < 0.;

                if !((all_pos_prev && all_pos) || (all_neg_prev && all_neg)) {
                    let mut v = [0.; 4];

                    for (idx, perm) in ids.iter().zip(perms.iter()) {
                        for m in 0..4 {
                            v[m] = u[s - idx[m].0 * si - idx[m].1 * sj - idx[m].2];
                        }

                        let r = tetrahedron_dist(v, &mut dual_norm, *perm);
                        if let Some(r) = r {
                            for m in 0..4 {
                                let q = s - idx[m].0 * si - idx[m].1 * sj - idx[m].2;
                                d[q] = min(d[q], r[m]);
                            }
                        }
                    }
                }
                all_pos_prev = all_pos;
                all_neg_prev = all_neg;
            }
        }
    }
}

/// Compute the anisotropic distance to the zero level set of a function on a axes-aligned
/// right triangle. The right angle is assumed to be at vertex 1.
///
/// `perm` specifies which leg of the triangle corresponds to the directions e₁ and e₂:
///
/// `[0, 1]`: 0-1 leg is parallel to e₁, 1-2 leg is parallel to e₂
/// `[1, 0]`: 0-1 leg is parallel to e₂, 1-2 leg is parallel to e₁
///
/// ```text
/// [0, 1]    [1, 0]
/// ------    ------
///
///     2      1--2
///    /|      | /
///   / |      |/
///  0--1      0
/// ```
///
fn triangle_dist<F>(mut u: [f64; 3], perm: [usize; 2], mut dual_norm: F) -> Option<[f64; 3]>
where
    F: FnMut([f64; 2]) -> f64,
{
    // check if sign differs (level set goes throught the triangle)
    if (u[0] > 0. && u[1] > 0. && u[2] > 0.) || (u[0] < 0. && u[1] < 0. && u[2] < 0.) {
        return None;
    }

    let g = [u[1] - u[0], u[2] - u[1]];
    let g = [g[perm[0]], g[perm[1]]];
    let norm = dual_norm(g);
    // all values are zero
    if norm == 0. {
        return Some([0., 0., 0.]);
    }
    let g_norm_rcp = 1. / norm;

    // TODO: This supports only even norms. Do we want to support triangle norms, for instance?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// As `init_dist_2d`, but for general anisotropic norm.
///
/// `dual_norm` is the __dual__ norm. It must be a positively one-homogeneous function.
pub fn init_dist_2d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 2]) -> f64,
{
    let (nx, ny) = dim;
    assert_eq!(nx * ny, u.len());
    assert_eq!(nx * ny, d.len());

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    for j in 1..nx {
        for i in 1..ny {
            let s = j * ny + i;
            let v = [s - ny - 1, s - ny, s];
            let r = triangle_dist([u[v[0]], u[v[1]], u[v[2]]], [1, 0], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
            let v = [s - ny - 1, s - 1, s];
            let r = triangle_dist([u[v[0]], u[v[1]], u[v[2]]], [0, 1], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::norm::{DualNorm, EuclideanNorm};

    #[test]
    fn simple_triangles() {
        let eucl_triangle_dist = |v| triangle_dist(v, [0, 1], |p| EuclideanNorm.dual_norm(p));
        assert_eq!(eucl_triangle_dist([0., 0., 0.]), Some([0., 0., 0.]));
        assert_eq!(eucl_triangle_dist([1., 1., 1.]), None);
        assert_eq!(eucl_triangle_dist([-1., -1., -1.]), None);
        assert_eq!(
            eucl_triangle_dist([0., 1., 0.]),
            Some([0., 1. / (2f64).sqrt(), 0.])
        );
        assert_eq!(eucl_triangle_dist([0., -1., -1.]), Some([0., 1., 1.]));
        assert_eq!(eucl_triangle_dist([0., 1., 1.]), Some([0., 1., 1.]));
        assert_eq!(eucl_triangle_dist([1., 1., 0.]), Some([1., 1., 0.]));
        assert_eq!(eucl_triangle_dist([-1., 0., 0.]), Some([1., 0., 0.]));
        assert_eq!(
            eucl_triangle_dist([1., 0., 1.]),
            Some([1. / (2f64).sqrt(), 0., 1. / (2f64).sqrt()])
        );
    }

    #[test]
    fn anisotropic_norm_2d() {
        // Du = (1, 0)
        let u = [0., 0., 1., 1.];
        let mut d = [0.; 4];
        init_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0., 1., 1.]);

        // Du = (0, 1)
        let u = [0., 1., 0., 1.];
        let mut d = [0.; 4];
        init_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0.5, 0., 0.5]);
    }

    #[test]
    fn anisotropic_norm_3d() {
        // Du = (1, 0, 0)
        let u = [0., 0., 0., 0., 1., 1., 1., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0., 0., 1., 1., 1., 1.]);

        // Du = (0, 1, 0)
        let u = [0., 0., 1., 1., 0., 0., 1., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0.5, 0.5, 0., 0., 0.5, 0.5]);

        // Du = (0, 0, 1)
        let u = [0., 1., 0., 1., 0., 1., 0., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0.25, 0., 0.25, 0., 0.25, 0., 0.25]);
    }
}
<|endoftext|>


<commit_before>extern mod ssl = "github.com/sfackler/rust-openssl#openssl:0.0";

use std::hash::Hash;
use std::hashmap::HashMap;
use std::rand::{task_rng, Rng};

use ssl::crypto::hash::{hash, MD5};

struct RingPartitioned<'a> {
    name: ~str,
    nodes: HashMap<uint, HashMap<~str, uint>>,

    /// Partitioning
    shift: uint,
    replicas: uint,
    part_power: uint,
    partitions: ~[uint],
}

trait Ring {

    /**
     * Generate an id for str
     **/
    fn get_id<'a>(&'a self, id: &str) -> u32;

    /**
     * Rebalance the ring.
     *
     * Rebalances the ring after adding or removing
     * nodes to it.
     **/
    fn rebalance<'a>(&'a mut self) -> ();

    /**
     * Gets a list of nodes for `id`
     **/
    fn get_nodes<'a>(&'a self, id: &str) -> ~[&'a HashMap<~str, uint>];

    /**
     * Gets a list of nodes for `id`
     **/
    fn add_node<'a>(&'a mut self, node: HashMap<~str, uint>) -> ();
}

impl<'a> Ring for RingPartitioned<'a> {

    fn get_id<'a>(&'a self, id: &str) -> u32 {
       let sh = hash(MD5, id.as_bytes());
       sh.hash() as u32 >> self.shift
    }

    fn rebalance<'a>(&'a mut self) -> () {

        // No nodes nor partitions. Nothing
        // to rebalance.
        if self.nodes.len() == 0 && self.partitions.len() == 0 { return; }

        let node_weight = 2;
        let total_weight = 2 * self.nodes.len();

        // http://github.com/mozilla/rust/issues/11499
        // This is equivalent to 2^self.part_power
        let mut total_partitions = 1 << self.part_power;
        total_partitions -= self.partitions.len();

        let desired_parts = total_partitions / total_weight * node_weight;
        for node in self.nodes.iter() {
            if total_partitions > 0 {
               for _ in range(1, desired_parts) {
                   if total_partitions <= 0 {
                       break;
                   }
                   self.partitions.push(*node.n0());
                   total_partitions -= 1;
               }
            }
        }

        task_rng().shuffle_mut(self.partitions);
    }

    fn add_node<'a>(&'a mut self, node: HashMap<~str, uint>) -> () {
        let new_id = self.nodes.len() + 2;
        self.nodes.insert(new_id, node);
    }

    fn get_nodes<'a>(&'a self, id: &str) -> ~[&'a HashMap<~str, uint>] {
        let mut part = self.get_id(id);
        let mut nodes = ~[];
        let mut node_id = self.partitions[part];

        nodes.push(self.nodes.get(&node_id));

        while nodes.len() < self.replicas as uint {
            part += 1;
            if part as uint > self.partitions.len() {
                part = 0;
            }

            node_id = self.partitions[part];
            if nodes.contains(&self.nodes.get(&node_id)) {
                continue;
            }

            nodes.push(self.nodes.get(&node_id));
        }

        nodes
    }

}

impl<'a> RingPartitioned<'a> {

    fn new(name: ~str, part_power: uint, replicas: uint) -> RingPartitioned<'a> {

        RingPartitioned {
            name: name,
            nodes: HashMap::new(),
            replicas: replicas,
            shift: 32 - part_power,
            part_power: part_power,
            partitions: ~[]}

    }
}

#[cfg(test)]
mod tests {
    use std::hashmap::HashMap;

    use ring::Ring;
    use ring::RingPartitioned;

    fn get_ring(nodes: uint) -> RingPartitioned {
        let mut ring = RingPartitioned::new(~"test", 16, 3);

        // Add some nodes
        for nid in range(1u, nodes) {
            let mut node = HashMap::new();
            node.insert(~"id", nid);
            ring.add_node(node);
        }

        ring

    }

    #[test]
    fn test_rebalance() {
        let mut ring = get_ring(4);
        ring.rebalance();
        assert_eq!(ring.partitions.len(), 65529)
    }

    #[test]
    fn test_get_nodes() {
        let mut ring = get_ring(4);
        ring.rebalance();
        let nodes = ring.get_nodes("0");
        assert_eq!(nodes.len(), 3)
    }
}
<commit_msg>1 Ring to rull them all<commit_after>extern mod ssl = "github.com/sfackler/rust-openssl#openssl:0.0";

use std::hash::Hash;
use std::hashmap::HashMap;
use std::rand::{task_rng, Rng};

use ssl::crypto::hash::{hash, MD5};

struct Ring<'a> {
    name: ~str,
    nodes: HashMap<uint, HashMap<~str, uint>>,

    /// Partitioning
    shift: uint,
    replicas: uint,
    part_power: uint,
    partitions: ~[uint],
}

impl<'a> Ring<'a> {

    fn new(name: ~str, part_power: uint, replicas: uint) -> Ring<'a> {

        Ring {
            name: name,
            nodes: HashMap::new(),
            replicas: replicas,
            shift: 32 - part_power,
            part_power: part_power,
            partitions: ~[]}

    }

    /**
     * Generate an id for str
     **/
    fn get_id<'a>(&'a self, id: &str) -> u32 {
       let sh = hash(MD5, id.as_bytes());
       sh.hash() as u32 >> self.shift
    }

    /**
     * Rebalance the ring.
     *
     * Rebalances the ring after adding or removing
     * nodes to it.
     **/
    fn rebalance<'a>(&'a mut self) -> () {

        // No nodes nor partitions. Nothing
        // to rebalance.
        if self.nodes.len() == 0 && self.partitions.len() == 0 { return; }

        let node_weight = 2;
        let total_weight = 2 * self.nodes.len();

        // http://github.com/mozilla/rust/issues/11499
        // This is equivalent to 2^self.part_power
        let mut total_partitions = 1 << self.part_power;
        total_partitions -= self.partitions.len();

        let desired_parts = total_partitions / total_weight * node_weight;
        for node in self.nodes.iter() {
            if total_partitions > 0 {
               for _ in range(1, desired_parts) {
                   if total_partitions <= 0 {
                       break;
                   }
                   self.partitions.push(*node.n0());
                   total_partitions -= 1;
               }
            }
        }

        task_rng().shuffle_mut(self.partitions);
    }

    /**
     * Gets a list of nodes for `id`
     **/
    fn get_nodes<'a>(&'a self, id: &str) -> ~[&'a HashMap<~str, uint>] {
        let mut part = self.get_id(id);
        let mut nodes = ~[];
        let mut node_id = self.partitions[part];

        nodes.push(self.nodes.get(&node_id));

        while nodes.len() < self.replicas as uint {
            part += 1;
            if part as uint > self.partitions.len() {
                part = 0;
            }

            node_id = self.partitions[part];
            if nodes.contains(&self.nodes.get(&node_id)) {
                continue;
            }

            nodes.push(self.nodes.get(&node_id));
        }

        nodes
    }

    /**
     * Gets a list of nodes for `id`
     **/
    fn add_node<'a>(&'a mut self, node: HashMap<~str, uint>) -> () {
        let new_id = self.nodes.len() + 2;
        self.nodes.insert(new_id, node);
    }
}

#[cfg(test)]
mod tests {
    use std::hashmap::HashMap;

    use ring::Ring;

    fn get_ring(nodes: uint) -> Ring {
        let mut ring = Ring::new(~"test", 16, 3);

        // Add some nodes
        for nid in range(1u, nodes) {
            let mut node = HashMap::new();
            node.insert(~"id", nid);
            ring.add_node(node);
        }

        ring

    }

    #[test]
    fn test_rebalance() {
        let mut ring = get_ring(4);
        ring.rebalance();
        assert_eq!(ring.partitions.len(), 65529)
    }

    #[test]
    fn test_get_nodes() {
        let mut ring = get_ring(4);
        ring.rebalance();
        let nodes = ring.get_nodes("0");
        assert_eq!(nodes.len(), 3)
    }
}
<|endoftext|>


<commit_before>use sass::output_style::SassOutputStyle;
use sass::variable::SassVariable;
use ast::node::Node;
use token::{Lexeme, Token};
use error::Result;
use context::Context;

use std::fmt;
use std::io::Write;

#[derive(Clone, PartialEq)]
pub struct SassRule {
    pub selectors: Vec<Lexeme>,
    pub children: Vec<Node>,
}

impl SassRule {
    pub fn new() -> SassRule {
        SassRule {
            selectors: Vec::new(),
            children: Vec::new(),
        }
    }

    fn selector_distribution(&self, parents: &str, separator: &str) -> String {
        match parents.len() {
            0 => self.selectors.iter().map(|s| s.token.to_string()).collect::<Vec<_>>().join(separator),
            _ => parents.split(",").map(|p| {
                self.selectors.iter().map(|s| {
                    if s.token.to_string().contains("&") {
                        s.token.to_string().replace("&", p.trim())
                    } else {
                        format!("{} {}", p.trim(), s.token)
                    }
                }).collect::<Vec<_>>().join(separator)
            }).collect::<Vec<_>>().join(separator),
        }
    }

    pub fn stream<W: Write>(&self, output: &mut W, style: SassOutputStyle) -> Result<()> {
        try!(self.recursive_stream(output, style, "", ""));
        Ok(try!(write!(output, "{}", style.rule_separator())))
    }

    pub fn recursive_stream<W: Write>(&self, output: &mut W, style: SassOutputStyle, parents: &str, nesting: &str) -> Result<()> {
        let mut selector_string = self.selector_distribution(parents, &style.selector_separator());
        if style == SassOutputStyle::Compressed {
            selector_string = compress_selectors(selector_string);
        }

        let mut properties = self.child_properties().into_iter();
        let mut has_properties = false;

        // TODO: peek?
        if let Some(prop) = properties.next() {
            has_properties = true;
            try!(write!(output, "{}{}{{{}{}",
              selector_string,
              style.selector_brace_separator(),
              style.brace_property_separator(),
              style.before_property(nesting),
            ));

            try!(prop.stream(output, style));

            for prop in properties {
                try!(write!(output, "{}{}",
                    style.after_property(),
                    style.before_property(nesting),
                ));
                try!(prop.stream(output, style));
            }
            try!(write!(output, "{}}}", style.property_brace_separator()));
        }

        let mut child_rules = self.child_rules().into_iter();
        if let Some(cr) = child_rules.next() {
            let mut recursive_nesting = String::from(nesting);
            if has_properties {
                recursive_nesting.push_str("  ");
                try!(write!(
                    output, "{}", style.rule_and_child_rules_separator(&recursive_nesting)
                ));
            }
            try!(cr.recursive_stream(output, style, &selector_string, &recursive_nesting));
            for cr in child_rules {
                try!(write!(output, "{}", style.child_rule_separator(has_properties)));
                try!(cr.recursive_stream(output, style, &selector_string, &recursive_nesting));
            }
        }

        Ok(())
    }

    // TODO: I don't like how child_properties and child_rules are so different, but
    // i'm not sure what to do about it yet.
    pub fn child_properties(&self) -> Vec<&Node> {
        self.children.iter().filter(|c|
            match **c {
                Node::Rule(..)     => false,
                Node::Comment(..)  => true,
                Node::Property(..) => true,
                Node::Variable(..) => true,
            }
        ).collect::<Vec<_>>()
    }

    pub fn child_rules(&self) -> Vec<SassRule> {
        self.children.clone().into_iter().filter_map(|c|
            match c {
                Node::Rule(rule)   => Some(rule),
                Node::Comment(..)  => None,
                Node::Property(..) => None,
                Node::Variable(..) => None,
            }
        ).collect::<Vec<_>>()
    }

    pub fn has_properties(&self) -> bool {
        !self.child_properties().is_empty()
    }

    pub fn optimize(self) -> Vec<SassRule> {
        let mut results = vec![];
        if self.has_properties() {
            results.push(self.clone());
            return results
        } else {
            self.child_rules().into_iter().flat_map(|cr|
                cr.collapse_with_parent_selectors(&self.selectors)
            ).collect::<Vec<_>>()
        }
    }

    pub fn collapse_with_parent_selectors(self, parents: &Vec<Lexeme>) -> Vec<SassRule> {
        let new_selectors = parents.iter().flat_map(|p|
            self.selectors.iter().map(|c|
                Lexeme {
                    token: Token::String(format!("{} {}", p.token, c.token)),
                    offset: p.offset,
                }
            ).collect::<Vec<_>>()
        ).collect();
        SassRule {
            selectors: new_selectors,
            children: self.children,
        }.optimize()
    }

    pub fn evaluate(self, context: &Context) -> SassRule {
        let mut local_context = (*context).clone();
        SassRule {
            selectors: self.selectors,
            children: self.children.into_iter().filter_map(|c|
                match c {
                    Node::Rule(sr) => Some(Node::Rule(sr.evaluate(&local_context))),
                    Node::Property(lex, ex) => {
                        Some(Node::Property(lex, ex.evaluate(&local_context)))
                    },
                    Node::Comment(sc) => Some(Node::Comment(sc)),
                    Node::Variable(sv) => {
                        let evaluated_var = sv.value.evaluate(&local_context);
                        local_context.add_variable(SassVariable {
                            name: sv.name,
                            value: evaluated_var,
                        });
                        None
                    },
                }
            ).collect(),
        }
    }
}

fn compress_selectors(selector_string: String) -> String {
    selector_string.replace(" > ", ">").replace(" + ", "+")
}

impl fmt::Debug for SassRule {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let children = self.children.iter().map(|c| format!("{:?}", c)).collect::<Vec<_>>().join("\n");
        let indented_children = children.split("\n").collect::<Vec<_>>().join("\n  ");
        write!(f, "\n{:?} {{\n  {}\n}}", self.selectors, indented_children)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::node::Node;
    use ast::expression::Expression;
    use token::{Token, Lexeme};

    #[test]
    fn it_collapses_subrules_without_properties() {
        let innermost_rule = SassRule {
            selectors: vec![Lexeme {
                token: Token::String("strong".into()),
                offset: Some(6)
            }],
            children: vec![
                Node::Property(
                    Lexeme { token: Token::String("font-weight".into()), offset: Some(12) },
                    Expression::String(
                        Lexeme { token: Token::String("bold".into()), offset: Some(19) }
                    ),
                ),
            ],
        };

        let further_in_rule = SassRule {
            selectors: vec![Lexeme {
                token: Token::String("img".into()),
                offset: Some(6)
            }],
            children: vec![
                Node::Property(
                    Lexeme { token: Token::String("color".into()), offset: Some(12) },
                    Expression::String(
                        Lexeme { token: Token::String("blue".into()), offset: Some(19) }
                    ),
                ),
                Node::Rule(innermost_rule),
            ],
        };

        let middle_rule = SassRule {
            selectors: vec![Lexeme { token: Token::String("span".into()), offset: Some(0) }],
            children: vec![Node::Rule(further_in_rule)],
        };

        let outer_rule = SassRule {
            selectors: vec![Lexeme { token: Token::String("div".into()), offset: Some(0) }],
            children: vec![Node::Rule(middle_rule)],
        };

        assert_eq!(
            outer_rule.optimize(),
            vec![
                SassRule {
                    selectors: vec![
                        Lexeme { token: Token::String("div span img".into()), offset: Some(0) },
                    ],
                    children: vec![
                        Node::Property(
                            Lexeme { token: Token::String("color".into()), offset: Some(12) },
                            Expression::String(
                                Lexeme { token: Token::String("blue".into()), offset: Some(19) }
                            ),
                        ),
                        Node::Rule(
                            SassRule {
                                selectors: vec![
                                    Lexeme {
                                        token: Token::String("strong".into()),
                                        offset: Some(6)
                                    },
                                ],
                                children: vec![Node::Property(
                                    Lexeme { token: Token::String("font-weight".into()), offset: Some(12) },
                                    Expression::String(
                                        Lexeme { token: Token::String("bold".into()), offset: Some(19) }
                                    ),
                                )],
                            }
                        ),
                    ],
                },
            ]
        );
    }
}
<commit_msg>Strip all comments for compressed output<commit_after>use sass::output_style::SassOutputStyle;
use sass::variable::SassVariable;
use ast::node::Node;
use token::{Lexeme, Token};
use error::Result;
use context::Context;

use std::fmt;
use std::io::Write;

#[derive(Clone, PartialEq)]
pub struct SassRule {
    pub selectors: Vec<Lexeme>,
    pub children: Vec<Node>,
}

impl SassRule {
    pub fn new() -> SassRule {
        SassRule {
            selectors: Vec::new(),
            children: Vec::new(),
        }
    }

    fn selector_distribution(&self, parents: &str, separator: &str) -> String {
        match parents.len() {
            0 => self.selectors.iter().map(|s| s.token.to_string()).collect::<Vec<_>>().join(separator),
            _ => parents.split(",").map(|p| {
                self.selectors.iter().map(|s| {
                    if s.token.to_string().contains("&") {
                        s.token.to_string().replace("&", p.trim())
                    } else {
                        format!("{} {}", p.trim(), s.token)
                    }
                }).collect::<Vec<_>>().join(separator)
            }).collect::<Vec<_>>().join(separator),
        }
    }

    pub fn stream<W: Write>(&self, output: &mut W, style: SassOutputStyle) -> Result<()> {
        try!(self.recursive_stream(output, style, "", ""));
        Ok(try!(write!(output, "{}", style.rule_separator())))
    }

    pub fn recursive_stream<W: Write>(&self, output: &mut W, style: SassOutputStyle, parents: &str, nesting: &str) -> Result<()> {
        let mut selector_string = self.selector_distribution(parents, &style.selector_separator());
        if style == SassOutputStyle::Compressed {
            selector_string = compress_selectors(selector_string);
        }

        let mut properties = self.child_properties().into_iter();
        let mut has_properties = false;

        if style == SassOutputStyle::Compressed {
            properties = properties.filter(|p|
                match **p {
                    Node::Comment(..) => false,
                    _ => true,
                }
            ).collect::<Vec<_>>().into_iter();
        }

        // TODO: peek?
        if let Some(prop) = properties.next() {
            has_properties = true;
            try!(write!(output, "{}{}{{{}{}",
              selector_string,
              style.selector_brace_separator(),
              style.brace_property_separator(),
              style.before_property(nesting),
            ));

            try!(prop.stream(output, style));

            for prop in properties {
                try!(write!(output, "{}{}",
                    style.after_property(),
                    style.before_property(nesting),
                ));
                try!(prop.stream(output, style));
            }
            try!(write!(output, "{}}}", style.property_brace_separator()));
        }

        let mut child_rules = self.child_rules().into_iter();
        if let Some(cr) = child_rules.next() {
            let mut recursive_nesting = String::from(nesting);
            if has_properties {
                recursive_nesting.push_str("  ");
                try!(write!(
                    output, "{}", style.rule_and_child_rules_separator(&recursive_nesting)
                ));
            }
            try!(cr.recursive_stream(output, style, &selector_string, &recursive_nesting));
            for cr in child_rules {
                try!(write!(output, "{}", style.child_rule_separator(has_properties)));
                try!(cr.recursive_stream(output, style, &selector_string, &recursive_nesting));
            }
        }

        Ok(())
    }

    // TODO: I don't like how child_properties and child_rules are so different, but
    // i'm not sure what to do about it yet.
    pub fn child_properties(&self) -> Vec<&Node> {
        self.children.iter().filter(|c|
            match **c {
                Node::Rule(..)     => false,
                Node::Comment(..)  => true,
                Node::Property(..) => true,
                Node::Variable(..) => true,
            }
        ).collect::<Vec<_>>()
    }

    pub fn child_rules(&self) -> Vec<SassRule> {
        self.children.clone().into_iter().filter_map(|c|
            match c {
                Node::Rule(rule)   => Some(rule),
                Node::Comment(..)  => None,
                Node::Property(..) => None,
                Node::Variable(..) => None,
            }
        ).collect::<Vec<_>>()
    }

    pub fn has_properties(&self) -> bool {
        !self.child_properties().is_empty()
    }

    pub fn optimize(self) -> Vec<SassRule> {
        let mut results = vec![];
        if self.has_properties() {
            results.push(self.clone());
            return results
        } else {
            self.child_rules().into_iter().flat_map(|cr|
                cr.collapse_with_parent_selectors(&self.selectors)
            ).collect::<Vec<_>>()
        }
    }

    pub fn collapse_with_parent_selectors(self, parents: &Vec<Lexeme>) -> Vec<SassRule> {
        let new_selectors = parents.iter().flat_map(|p|
            self.selectors.iter().map(|c|
                Lexeme {
                    token: Token::String(format!("{} {}", p.token, c.token)),
                    offset: p.offset,
                }
            ).collect::<Vec<_>>()
        ).collect();
        SassRule {
            selectors: new_selectors,
            children: self.children,
        }.optimize()
    }

    pub fn evaluate(self, context: &Context) -> SassRule {
        let mut local_context = (*context).clone();
        SassRule {
            selectors: self.selectors,
            children: self.children.into_iter().filter_map(|c|
                match c {
                    Node::Rule(sr) => Some(Node::Rule(sr.evaluate(&local_context))),
                    Node::Property(lex, ex) => {
                        Some(Node::Property(lex, ex.evaluate(&local_context)))
                    },
                    Node::Comment(sc) => Some(Node::Comment(sc)),
                    Node::Variable(sv) => {
                        let evaluated_var = sv.value.evaluate(&local_context);
                        local_context.add_variable(SassVariable {
                            name: sv.name,
                            value: evaluated_var,
                        });
                        None
                    },
                }
            ).collect(),
        }
    }
}

fn compress_selectors(selector_string: String) -> String {
    selector_string.replace(" > ", ">").replace(" + ", "+")
}

impl fmt::Debug for SassRule {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let children = self.children.iter().map(|c| format!("{:?}", c)).collect::<Vec<_>>().join("\n");
        let indented_children = children.split("\n").collect::<Vec<_>>().join("\n  ");
        write!(f, "\n{:?} {{\n  {}\n}}", self.selectors, indented_children)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::node::Node;
    use ast::expression::Expression;
    use token::{Token, Lexeme};

    #[test]
    fn it_collapses_subrules_without_properties() {
        let innermost_rule = SassRule {
            selectors: vec![Lexeme {
                token: Token::String("strong".into()),
                offset: Some(6)
            }],
            children: vec![
                Node::Property(
                    Lexeme { token: Token::String("font-weight".into()), offset: Some(12) },
                    Expression::String(
                        Lexeme { token: Token::String("bold".into()), offset: Some(19) }
                    ),
                ),
            ],
        };

        let further_in_rule = SassRule {
            selectors: vec![Lexeme {
                token: Token::String("img".into()),
                offset: Some(6)
            }],
            children: vec![
                Node::Property(
                    Lexeme { token: Token::String("color".into()), offset: Some(12) },
                    Expression::String(
                        Lexeme { token: Token::String("blue".into()), offset: Some(19) }
                    ),
                ),
                Node::Rule(innermost_rule),
            ],
        };

        let middle_rule = SassRule {
            selectors: vec![Lexeme { token: Token::String("span".into()), offset: Some(0) }],
            children: vec![Node::Rule(further_in_rule)],
        };

        let outer_rule = SassRule {
            selectors: vec![Lexeme { token: Token::String("div".into()), offset: Some(0) }],
            children: vec![Node::Rule(middle_rule)],
        };

        assert_eq!(
            outer_rule.optimize(),
            vec![
                SassRule {
                    selectors: vec![
                        Lexeme { token: Token::String("div span img".into()), offset: Some(0) },
                    ],
                    children: vec![
                        Node::Property(
                            Lexeme { token: Token::String("color".into()), offset: Some(12) },
                            Expression::String(
                                Lexeme { token: Token::String("blue".into()), offset: Some(19) }
                            ),
                        ),
                        Node::Rule(
                            SassRule {
                                selectors: vec![
                                    Lexeme {
                                        token: Token::String("strong".into()),
                                        offset: Some(6)
                                    },
                                ],
                                children: vec![Node::Property(
                                    Lexeme { token: Token::String("font-weight".into()), offset: Some(12) },
                                    Expression::String(
                                        Lexeme { token: Token::String("bold".into()), offset: Some(19) }
                                    ),
                                )],
                            }
                        ),
                    ],
                },
            ]
        );
    }
}
<|endoftext|>