<commit_before>use std::mem;

use {Vector3f, Point2f};
use bsdf::{BxDF, BxDFType, BSDF_SPECULAR, BSDF_REFLECTION, BSDF_TRANSMISSION};
use geometry::*;
use spectrum::Spectrum;
use na::{Dot, Norm, clamp};

/// Compute the reflection direction
pub fn reflect(wo: &Vector3f, n: &Vector3f) -> Vector3f {
    (-(*wo) + *n * 2.0 * wo.dot(n)).normalize()
}

/// Compute the refraction direction
pub fn refract(i: &Vector3f, n: &Vector3f, eta: f32) -> Option<Vector3f> {
    let cos_theta_i = n.dot(i);
    let sin2theta_i = (1.0 - cos_theta_i * cos_theta_i).max(0.0);
    let sin2theta_t = eta * eta * sin2theta_i;

    if sin2theta_t >= 1.0 {
        None
    } else {
        let cos_theta_t = (1.0 - sin2theta_t).sqrt();
        Some(eta * -*i + (eta * cos_theta_i - cos_theta_t) * *n)
    }
}

/// Compute the Fresnel coefficient for dielectric materials
pub fn fr_dielectric(cos_theta_i: f32, eta_i: f32, eta_t: f32) -> f32 {
    let mut cos_theta_i = clamp(cos_theta_i, -1.0, 1.0);
    let (mut eta_i, mut eta_t) = (eta_i, eta_t);
    if cos_theta_i <= 0.0 {
        // If leaving the surface, swap the indices of refraction
        mem::swap(&mut eta_i, &mut eta_t);
        cos_theta_i = cos_theta_i.abs();
    }

    let sin_theta_i = (1.0 - cos_theta_i * cos_theta_i).max(0.0).sqrt();
    let sin_theta_t = eta_i / eta_t * sin_theta_i;
    if sin_theta_t >= 1.0 {
        // Total internal reflection
        1.0
    } else {
        let cos_theta_t = (1.0 - sin_theta_t * sin_theta_t).max(0.0).sqrt();
        // Reflectance for parallel polarized light
        let r_parl = ((eta_t * cos_theta_i) - (eta_i * cos_theta_t)) /
                     ((eta_t * cos_theta_i) + (eta_i * cos_theta_t));
        // Reflectance for perpendicular polarized light
        let r_perp = ((eta_i * cos_theta_i) - (eta_t * cos_theta_t)) /
                     ((eta_i * cos_theta_i) + (eta_t * cos_theta_t));
        // Total reflectance for unpolarized light
        0.5 * (r_parl * r_parl + r_perp * r_perp)
    }
}

fn fr_conductor(cos_theta_i: f32, eta_i: &Spectrum, eta_t: &Spectrum, k: &Spectrum) -> Spectrum {
    let cos_theta_i = clamp(cos_theta_i, -1.0, 1.0);
    let eta = *eta_t / *eta_i;
    let eta_k = *k / *eta_i;

    let cos2_theta_i = cos_theta_i * cos_theta_i;
    let sin2_theta_i = 1.0 - cos2_theta_i;
    let eta2 = eta * eta;
    let eta_k2 = eta_k * eta_k;

    let t0 = eta2 - eta_k2 - sin2_theta_i;
    let a2plusb2 = (t0 * t0 + 4.0 * eta2 * eta_k2).sqrt();
    let t1 = a2plusb2 + cos2_theta_i;
    let a = (0.5 * (a2plusb2 + t0)).sqrt();
    let t2 = 2.0 * cos_theta_i * a;
    let r_s = (t1 - t2) / (t1 + t2);

    let t3 = cos2_theta_i * a2plusb2 + sin2_theta_i * sin2_theta_i;
    let t4 = t2 * sin2_theta_i;
    let r_p = r_s * (t3 - t4) / (t3 + t4);

    0.5 * (r_p + r_s)
}

/// Trait for Fresnel materials
pub trait Fresnel {
    fn evaluate(&self, cos_theta_i: f32) -> Spectrum;
}

impl Fresnel {
    pub fn conductor(eta_i: Spectrum, eta_t: Spectrum, k: Spectrum) -> FresnelConductor {
        FresnelConductor {
            eta_i: eta_i,
            eta_t: eta_t,
            k: k,
        }
    }

    pub fn dielectric(eta_i: f32, eta_t: f32) -> FresnelDielectric {
        FresnelDielectric {
            eta_i: eta_i,
            eta_t: eta_t,
        }
    }
}

/// Fresnel for conductor materials
pub struct FresnelConductor {
    eta_i: Spectrum,
    eta_t: Spectrum,
    k: Spectrum,
}

impl FresnelConductor {
    pub fn new(eta_i: Spectrum, eta_t: Spectrum, k: Spectrum) -> FresnelConductor {
        FresnelConductor {
            eta_i: eta_i,
            eta_t: eta_t,
            k: k,
        }
    }
}

impl Fresnel for FresnelConductor {
    fn evaluate(&self, cos_theta_i: f32) -> Spectrum {
        fr_conductor(cos_theta_i.abs(), &self.eta_i, &self.eta_t, &self.k)
    }
}

/// Fresnel for dielectric materials
pub struct FresnelDielectric {
    eta_i: f32,
    eta_t: f32,
}

impl Fresnel for FresnelDielectric {
    fn evaluate(&self, cos_theta_i: f32) -> Spectrum {
        Spectrum::grey(fr_dielectric(cos_theta_i.abs(), self.eta_i, self.eta_t))
    }
}

/// BRDF for perfect specular reflection
pub struct SpecularReflection {
    r: Spectrum,
    fresnel: Box<Fresnel + Send + Sync>,
}

impl SpecularReflection {
    pub fn new(r: Spectrum, fresnel: Box<Fresnel + Send + Sync>) -> SpecularReflection {
        SpecularReflection {
            r: r,
            fresnel: fresnel,
        }
    }
}

impl BxDF for SpecularReflection {
    fn f(&self, _wo: &Vector3f, _wi: &Vector3f) -> Spectrum {
        // The probability to call f() with the exact (wo, wi) for specular reflection is 0, so we
        // return black here. Use sample_f() instead.
        Spectrum::black()
    }

    fn sample_f(&self, wo: &Vector3f, _sample: &Point2f) -> (Spectrum, Vector3f, f32, BxDFType) {
        // There's only one possible wi for a given wo, so we always return it with a pdf of 1.
        let wi = Vector3f::new(-wo.x, -wo.y, wo.z);
        let spectrum = self.fresnel.evaluate(cos_theta(&wi)) * self.r / abs_cos_theta(&wi);
        (spectrum, wi, 1.0, BxDFType::empty())
    }

    fn pdf(&self, wo: &Vector3f, wi: &Vector3f) -> f32 {
        0.0
    }

    fn get_type(&self) -> BxDFType {
        BSDF_SPECULAR | BSDF_REFLECTION
    }
}

pub struct SpecularTransmission {
    t: Spectrum,
    eta_a: f32,
    eta_b: f32,
    fresnel: FresnelDielectric,
}

impl SpecularTransmission {
    pub fn new(t: Spectrum, eta_a: f32, eta_b: f32) -> SpecularTransmission {
        SpecularTransmission {
            t: t,
            eta_a: eta_a,
            eta_b: eta_b,
            fresnel: Fresnel::dielectric(eta_a, eta_b),
        }
    }
}

impl BxDF for SpecularTransmission {
    fn f(&self, _wo: &Vector3f, _wi: &Vector3f) -> Spectrum {
        // The probability to call f() with the exact (wo, wi) for specular transmission is 0, so we
        // return black here. Use sample_f() instead.
        Spectrum::black()
    }

    fn sample_f(&self, wo: &Vector3f, _sample: &Point2f) -> (Spectrum, Vector3f, f32, BxDFType) {
        // Figure out which $\eta$ is incident and which is transmitted
        let entering = cos_theta(wo) > 0.0;
        let eta_i = if entering { self.eta_a } else { self.eta_b };
        let eta_t = if entering { self.eta_b } else { self.eta_a };

        // Compute ray direction for specular transmission
        if let Some(wi) = refract(wo,
                                  &face_forward(&Vector3f::new(0.0, 0.0, 1.0), wo),
                                  eta_i / eta_t) {
            let mut ft = self.t * (Spectrum::white() - self.fresnel.evaluate(cos_theta(&wi)));

            // Account for non-symmetry with transmission to different medium TODO
            ft = ft * (eta_i * eta_i) / (eta_t * eta_t);
            info!("wo={}. wi={}, cos_theta(wo)={}, cos_theta(wi)={}, abs_cos_theta(wi)={}, ft={}",
                  wo,
                  wi,
                  cos_theta(wo),
                  cos_theta(&wi),
                  abs_cos_theta(&wi),
                  ft);

            return (ft / abs_cos_theta(&wi), wi, 1.0, BSDF_SPECULAR | BSDF_TRANSMISSION);
        } else {
            return (Spectrum::white(), Vector3f::new(0.0, 0.0, 0.0), 0.0, BxDFType::empty());
        }
    }

    fn pdf(&self, wo: &Vector3f, wi: &Vector3f) -> f32 {
        0.0
    }

    fn get_type(&self) -> BxDFType {
        BSDF_SPECULAR | BSDF_TRANSMISSION
    }
}

pub struct FresnelSpecular {
    r: Spectrum,
    t: Spectrum,
    eta_a: f32,
    eta_b: f32,
}

impl FresnelSpecular {
    pub fn new() -> FresnelSpecular {
        FresnelSpecular {
            r: Spectrum::white(),
            t: Spectrum::white(),
            eta_a: 1.0,
            eta_b: 1.5,
        }
    }
}

impl BxDF for FresnelSpecular {
    fn f(&self, _wo: &Vector3f, _wi: &Vector3f) -> Spectrum {
        // The probability to call f() with the exact (wo, wi) for specular reflection is 0, so we
        // return black here. Use sample_f() instead.
        Spectrum::black()
    }

    fn sample_f(&self, wo: &Vector3f, u: &Point2f) -> (Spectrum, Vector3f, f32, BxDFType) {
        let fr = fr_dielectric(cos_theta(wo), self.eta_a, self.eta_b);
        if u[0] < fr {
            // Compute specular reflection for FresnelSpecular

            // Compute perfect specular reflection direction
            let wi = Vector3f::new(-wo.x, -wo.y, wo.z);

            return (fr * self.r * abs_cos_theta(&wi), wi, fr, BSDF_SPECULAR | BSDF_REFLECTION);
        } else {
            // Compute specular transmission for FresnelSpecular

            // Figure out which $\eta$ is incident and which is transmitted
            let entering = cos_theta(wo) > 0.0;
            let eta_i = if entering { self.eta_a } else { self.eta_b };
            let eta_t = if entering { self.eta_b } else { self.eta_a };

            // Compute ray direction for specular transmission
            if let Some(wi) = refract(wo,
                                      &face_forward(&Vector3f::new(0.0, 0.0, 1.0), wo),
                                      eta_i / eta_t) {
                let ft = self.t * (1.0 - fr);

                // Account for non-symmetry with transmission to different medium
                // TODO
                //
                return (ft / abs_cos_theta(&wi), wi, 1.0 - fr, BSDF_SPECULAR | BSDF_TRANSMISSION);
            } else {
                return (Spectrum::white(), Vector3f::new(0.0, 0.0, 0.0), 0.0, BxDFType::empty());
            }
        }
    }

    fn pdf(&self, wo: &Vector3f, wi: &Vector3f) -> f32 {
        0.0
    }

    fn get_type(&self) -> BxDFType {
        BSDF_SPECULAR | BSDF_REFLECTION | BSDF_TRANSMISSION
    }
}

#[test]
fn test_reflect() {
    let wo = Vector3f::new(-0.0137817357, -0.999056697, 0.0411854088);
    let n = Vector3f::new(-0.000777989568, 0.000739870942, 0.999999404);
    let wi = ::bsdf::reflect(&wo, &n);

    assert_relative_eq!(0.0137187857, wi.x);
    assert_relative_eq!(0.99911654, wi.y);
    assert_relative_eq!(0.0397284105, wi.z);
}
<commit_msg>Downgrade log<commit_after>use std::mem;

use {Vector3f, Point2f};
use bsdf::{BxDF, BxDFType, BSDF_SPECULAR, BSDF_REFLECTION, BSDF_TRANSMISSION};
use geometry::*;
use spectrum::Spectrum;
use na::clamp;

/// Compute the reflection direction
pub fn reflect(wo: &Vector3f, n: &Vector3f) -> Vector3f {
    (-(*wo) + *n * 2.0 * wo.dot(n)).normalize()
}

/// Compute the refraction direction
pub fn refract(i: &Vector3f, n: &Vector3f, eta: f32) -> Option<Vector3f> {
    let cos_theta_i = n.dot(i);
    let sin2theta_i = (1.0 - cos_theta_i * cos_theta_i).max(0.0);
    let sin2theta_t = eta * eta * sin2theta_i;

    if sin2theta_t >= 1.0 {
        None
    } else {
        let cos_theta_t = (1.0 - sin2theta_t).sqrt();
        Some(eta * -*i + (eta * cos_theta_i - cos_theta_t) * *n)
    }
}

/// Compute the Fresnel coefficient for dielectric materials
pub fn fr_dielectric(cos_theta_i: f32, eta_i: f32, eta_t: f32) -> f32 {
    let mut cos_theta_i = clamp(cos_theta_i, -1.0, 1.0);
    let (mut eta_i, mut eta_t) = (eta_i, eta_t);
    if cos_theta_i <= 0.0 {
        // If leaving the surface, swap the indices of refraction
        mem::swap(&mut eta_i, &mut eta_t);
        cos_theta_i = cos_theta_i.abs();
    }

    let sin_theta_i = (1.0 - cos_theta_i * cos_theta_i).max(0.0).sqrt();
    let sin_theta_t = eta_i / eta_t * sin_theta_i;
    if sin_theta_t >= 1.0 {
        // Total internal reflection
        1.0
    } else {
        let cos_theta_t = (1.0 - sin_theta_t * sin_theta_t).max(0.0).sqrt();
        // Reflectance for parallel polarized light
        let r_parl = ((eta_t * cos_theta_i) - (eta_i * cos_theta_t)) /
                     ((eta_t * cos_theta_i) + (eta_i * cos_theta_t));
        // Reflectance for perpendicular polarized light
        let r_perp = ((eta_i * cos_theta_i) - (eta_t * cos_theta_t)) /
                     ((eta_i * cos_theta_i) + (eta_t * cos_theta_t));
        // Total reflectance for unpolarized light
        0.5 * (r_parl * r_parl + r_perp * r_perp)
    }
}

fn fr_conductor(cos_theta_i: f32, eta_i: &Spectrum, eta_t: &Spectrum, k: &Spectrum) -> Spectrum {
    let cos_theta_i = clamp(cos_theta_i, -1.0, 1.0);
    let eta = *eta_t / *eta_i;
    let eta_k = *k / *eta_i;

    let cos2_theta_i = cos_theta_i * cos_theta_i;
    let sin2_theta_i = 1.0 - cos2_theta_i;
    let eta2 = eta * eta;
    let eta_k2 = eta_k * eta_k;

    let t0 = eta2 - eta_k2 - sin2_theta_i;
    let a2plusb2 = (t0 * t0 + 4.0 * eta2 * eta_k2).sqrt();
    let t1 = a2plusb2 + cos2_theta_i;
    let a = (0.5 * (a2plusb2 + t0)).sqrt();
    let t2 = 2.0 * cos_theta_i * a;
    let r_s = (t1 - t2) / (t1 + t2);

    let t3 = cos2_theta_i * a2plusb2 + sin2_theta_i * sin2_theta_i;
    let t4 = t2 * sin2_theta_i;
    let r_p = r_s * (t3 - t4) / (t3 + t4);

    0.5 * (r_p + r_s)
}

/// Trait for Fresnel materials
pub trait Fresnel {
    fn evaluate(&self, cos_theta_i: f32) -> Spectrum;
}

impl Fresnel {
    pub fn conductor(eta_i: Spectrum, eta_t: Spectrum, k: Spectrum) -> FresnelConductor {
        FresnelConductor {
            eta_i: eta_i,
            eta_t: eta_t,
            k: k,
        }
    }

    pub fn dielectric(eta_i: f32, eta_t: f32) -> FresnelDielectric {
        FresnelDielectric {
            eta_i: eta_i,
            eta_t: eta_t,
        }
    }
}

/// Fresnel for conductor materials
pub struct FresnelConductor {
    eta_i: Spectrum,
    eta_t: Spectrum,
    k: Spectrum,
}

impl FresnelConductor {
    pub fn new(eta_i: Spectrum, eta_t: Spectrum, k: Spectrum) -> FresnelConductor {
        FresnelConductor {
            eta_i: eta_i,
            eta_t: eta_t,
            k: k,
        }
    }
}

impl Fresnel for FresnelConductor {
    fn evaluate(&self, cos_theta_i: f32) -> Spectrum {
        fr_conductor(cos_theta_i.abs(), &self.eta_i, &self.eta_t, &self.k)
    }
}

/// Fresnel for dielectric materials
pub struct FresnelDielectric {
    eta_i: f32,
    eta_t: f32,
}

impl Fresnel for FresnelDielectric {
    fn evaluate(&self, cos_theta_i: f32) -> Spectrum {
        Spectrum::grey(fr_dielectric(cos_theta_i.abs(), self.eta_i, self.eta_t))
    }
}

/// BRDF for perfect specular reflection
pub struct SpecularReflection {
    r: Spectrum,
    fresnel: Box<Fresnel + Send + Sync>,
}

impl SpecularReflection {
    pub fn new(r: Spectrum, fresnel: Box<Fresnel + Send + Sync>) -> SpecularReflection {
        SpecularReflection {
            r: r,
            fresnel: fresnel,
        }
    }
}

impl BxDF for SpecularReflection {
    fn f(&self, _wo: &Vector3f, _wi: &Vector3f) -> Spectrum {
        // The probability to call f() with the exact (wo, wi) for specular reflection is 0, so we
        // return black here. Use sample_f() instead.
        Spectrum::black()
    }

    fn sample_f(&self, wo: &Vector3f, _sample: &Point2f) -> (Spectrum, Vector3f, f32, BxDFType) {
        // There's only one possible wi for a given wo, so we always return it with a pdf of 1.
        let wi = Vector3f::new(-wo.x, -wo.y, wo.z);
        let spectrum = self.fresnel.evaluate(cos_theta(&wi)) * self.r / abs_cos_theta(&wi);
        (spectrum, wi, 1.0, BxDFType::empty())
    }

    fn pdf(&self, wo: &Vector3f, wi: &Vector3f) -> f32 {
        0.0
    }

    fn get_type(&self) -> BxDFType {
        BSDF_SPECULAR | BSDF_REFLECTION
    }
}

pub struct SpecularTransmission {
    t: Spectrum,
    eta_a: f32,
    eta_b: f32,
    fresnel: FresnelDielectric,
}

impl SpecularTransmission {
    pub fn new(t: Spectrum, eta_a: f32, eta_b: f32) -> SpecularTransmission {
        SpecularTransmission {
            t: t,
            eta_a: eta_a,
            eta_b: eta_b,
            fresnel: Fresnel::dielectric(eta_a, eta_b),
        }
    }
}

impl BxDF for SpecularTransmission {
    fn f(&self, _wo: &Vector3f, _wi: &Vector3f) -> Spectrum {
        // The probability to call f() with the exact (wo, wi) for specular transmission is 0, so we
        // return black here. Use sample_f() instead.
        Spectrum::black()
    }

    fn sample_f(&self, wo: &Vector3f, _sample: &Point2f) -> (Spectrum, Vector3f, f32, BxDFType) {
        // Figure out which $\eta$ is incident and which is transmitted
        let entering = cos_theta(wo) > 0.0;
        let eta_i = if entering { self.eta_a } else { self.eta_b };
        let eta_t = if entering { self.eta_b } else { self.eta_a };

        // Compute ray direction for specular transmission
        if let Some(wi) = refract(wo,
                                  &face_forward(&Vector3f::new(0.0, 0.0, 1.0), wo),
                                  eta_i / eta_t) {
            let mut ft = self.t * (Spectrum::white() - self.fresnel.evaluate(cos_theta(&wi)));

            // Account for non-symmetry with transmission to different medium TODO
            ft = ft * (eta_i * eta_i) / (eta_t * eta_t);
            debug!("wo={}. wi={}, cos_theta(wo)={}, cos_theta(wi)={}, abs_cos_theta(wi)={}, ft={}",
                   wo,
                   wi,
                   cos_theta(wo),
                   cos_theta(&wi),
                   abs_cos_theta(&wi),
                   ft);

            return (ft / abs_cos_theta(&wi), wi, 1.0, BSDF_SPECULAR | BSDF_TRANSMISSION);
        } else {
            return (Spectrum::white(), Vector3f::new(0.0, 0.0, 0.0), 0.0, BxDFType::empty());
        }
    }

    fn pdf(&self, wo: &Vector3f, wi: &Vector3f) -> f32 {
        0.0
    }

    fn get_type(&self) -> BxDFType {
        BSDF_SPECULAR | BSDF_TRANSMISSION
    }
}

pub struct FresnelSpecular {
    r: Spectrum,
    t: Spectrum,
    eta_a: f32,
    eta_b: f32,
}

impl FresnelSpecular {
    pub fn new() -> FresnelSpecular {
        FresnelSpecular {
            r: Spectrum::white(),
            t: Spectrum::white(),
            eta_a: 1.0,
            eta_b: 1.5,
        }
    }
}

impl BxDF for FresnelSpecular {
    fn f(&self, _wo: &Vector3f, _wi: &Vector3f) -> Spectrum {
        // The probability to call f() with the exact (wo, wi) for specular reflection is 0, so we
        // return black here. Use sample_f() instead.
        Spectrum::black()
    }

    fn sample_f(&self, wo: &Vector3f, u: &Point2f) -> (Spectrum, Vector3f, f32, BxDFType) {
        let fr = fr_dielectric(cos_theta(wo), self.eta_a, self.eta_b);
        if u[0] < fr {
            // Compute specular reflection for FresnelSpecular

            // Compute perfect specular reflection direction
            let wi = Vector3f::new(-wo.x, -wo.y, wo.z);

            return (fr * self.r * abs_cos_theta(&wi), wi, fr, BSDF_SPECULAR | BSDF_REFLECTION);
        } else {
            // Compute specular transmission for FresnelSpecular

            // Figure out which $\eta$ is incident and which is transmitted
            let entering = cos_theta(wo) > 0.0;
            let eta_i = if entering { self.eta_a } else { self.eta_b };
            let eta_t = if entering { self.eta_b } else { self.eta_a };

            // Compute ray direction for specular transmission
            if let Some(wi) = refract(wo,
                                      &face_forward(&Vector3f::new(0.0, 0.0, 1.0), wo),
                                      eta_i / eta_t) {
                let ft = self.t * (1.0 - fr);

                // Account for non-symmetry with transmission to different medium
                // TODO
                //
                return (ft / abs_cos_theta(&wi), wi, 1.0 - fr, BSDF_SPECULAR | BSDF_TRANSMISSION);
            } else {
                return (Spectrum::white(), Vector3f::new(0.0, 0.0, 0.0), 0.0, BxDFType::empty());
            }
        }
    }

    fn pdf(&self, wo: &Vector3f, wi: &Vector3f) -> f32 {
        0.0
    }

    fn get_type(&self) -> BxDFType {
        BSDF_SPECULAR | BSDF_REFLECTION | BSDF_TRANSMISSION
    }
}

#[test]
fn test_reflect() {
    let wo = Vector3f::new(-0.0137817357, -0.999056697, 0.0411854088);
    let n = Vector3f::new(-0.000777989568, 0.000739870942, 0.999999404);
    let wi = ::bsdf::reflect(&wo, &n);

    assert_relative_eq!(0.0137187857, wi.x);
    assert_relative_eq!(0.99911654, wi.y);
    assert_relative_eq!(0.0397284105, wi.z);
}
<|endoftext|>


<commit_before>use cteepbd::{self, cte, types::RenNrenCo2, Balance, Components, Factors, VERSION};
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn get_version() -> String {
    VERSION.to_string()
}

#[wasm_bindgen]
pub fn set_panic_hook() {
    // When the `console_error_panic_hook` feature is enabled, we can call the
    // `set_panic_hook` function at least once during initialization, and then
    // we will get better error messages if our code ever panics.
    //
    // For more details see
    // https://github.com/rustwasm/console_error_panic_hook#readme
    #[cfg(feature = "console_error_panic_hook")]
    console_error_panic_hook::set_once();
}

// Convierte cadena a componentes
#[wasm_bindgen]
pub fn parse_components(datastring: &str) -> Result<JsValue, JsValue> {
    let comps: Components = datastring
        .parse::<Components>()
        .map_err(|e| e.to_string())?;
    let jscomps = JsValue::from_serde(&comps).map_err(|e| e.to_string())?;
    Ok(jscomps)
}

// Estructuras para gestión de factores de usuario
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
struct WFactorsCogenUserOptions {
    A_RED: Option<RenNrenCo2>,
    A_NEPB: Option<RenNrenCo2>,
}

#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
struct WFactorsRedUserOptions {
    RED1: Option<RenNrenCo2>,
    RED2: Option<RenNrenCo2>,
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
struct WFactorsUserOptions {
    cogen: Option<WFactorsCogenUserOptions>,
    red: Option<WFactorsRedUserOptions>,
    // strip_nepb: Option<bool>,
}

// Obtén factores a partir de la localización y los factores de usuario
//
// El campo de opciones tiene que ser al menos {}
#[wasm_bindgen]
pub fn new_wfactors(loc: &str, options: &JsValue) -> Result<JsValue, JsValue> {
    let defaults_wf = cte::CTE_USERWF;
    let rsoptions: WFactorsUserOptions = options.into_serde().map_err(|e| e.to_string())?;
    let red1 = rsoptions
        .red
        .and_then(|v| v.RED1)
        .or(Some(defaults_wf.red1));
    let red2 = rsoptions
        .red
        .and_then(|v| v.RED2)
        .or(Some(defaults_wf.red2));
    let cogen_to_grid = rsoptions.cogen.and_then(|v| v.A_RED).or(None);
    let cogen_to_nepb = rsoptions.cogen.and_then(|v| v.A_NEPB).or(None);
    let user_wf = cteepbd::UserWF {
        red1,
        red2,
        cogen_to_grid,
        cogen_to_nepb,
    };
    // Puede tener errores de parsing o de localidad
    let fp: Factors = cte::wfactors_from_loc(loc, &cte::CTE_LOCWF_RITE2014, user_wf, defaults_wf)
        .map_err(|e| e.to_string())?;
    let jsfactors = JsValue::from_serde(&fp).map_err(|e| e.to_string())?;
    Ok(jsfactors)
}

/// Calcula eficiencia energética
///
/// Calcula también la demanda renovable de ACS si es posible y agrega los datos al balance.
/// Si no se puede calcular esta demanda renovable balance.misc es un valor nulo (null en JS, None en Rust)
/// Si se puede agregar, se añade:
/// - balance.misc.demanda_anual_acs: demanda anual de ACS en kWh/a
/// - balance.misc.porcentaje_renovable_demanda_acs_nrb: fracción renovable de la demanda de ACS (kWh/kWh)
#[wasm_bindgen]
pub fn energy_performance(
    components: &JsValue,
    wfactors: &JsValue,
    kexp: f32,
    area: f32,
    dhw_needs: Option<f32>,
) -> Result<JsValue, JsValue> {
    let comps: Components = components.into_serde().map_err(|e| e.to_string())?;
    let wfacs: Factors = wfactors.into_serde().map_err(|e| e.to_string())?;
    let balance: Balance =
        cteepbd::energy_performance(&comps, &wfacs, kexp, area)
        .map(|b| {cte::incorpora_demanda_renovable_acs_nrb(b, dhw_needs)})
        .map_err(|e| e.to_string())?;
    let jsbalance = JsValue::from_serde(&balance).map_err(|e| e.to_string())?;
    Ok(jsbalance)
}

// Calcula eficiencia energética para el perímetro próximo y servicio de ACS
#[wasm_bindgen]
pub fn energy_performance_acs_nrb(
    components: &JsValue,
    wfactors: &JsValue,
    kexp: f32,
    area: f32,
) -> Result<JsValue, JsValue> {
    let comps: Components = components.into_serde().map_err(|e| e.to_string())?;
    let wfacs: Factors = wfactors.into_serde().map_err(|e| e.to_string())?;

    // componentes para ACS
    let comps_acs = comps
        .filter_by_epb_service(cteepbd::types::Service::ACS)
        .normalize();
    let wfacs_nrb = cte::wfactors_to_nearby(&wfacs);
    let balance: Balance = cteepbd::energy_performance(&comps_acs, &wfacs_nrb, kexp, area).unwrap();
    let jsbalance = JsValue::from_serde(&balance).map_err(|e| e.to_string())?;
    Ok(jsbalance)
}
<commit_msg>wasm-cteepbd: Elimina cálculo de fracción renovable de ACS en bindings<commit_after>use cteepbd::{self, cte, types::RenNrenCo2, Balance, Components, Factors, VERSION};
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn get_version() -> String {
    VERSION.to_string()
}

#[wasm_bindgen]
pub fn set_panic_hook() {
    // When the `console_error_panic_hook` feature is enabled, we can call the
    // `set_panic_hook` function at least once during initialization, and then
    // we will get better error messages if our code ever panics.
    //
    // For more details see
    // https://github.com/rustwasm/console_error_panic_hook#readme
    #[cfg(feature = "console_error_panic_hook")]
    console_error_panic_hook::set_once();
}

// Convierte cadena a componentes
#[wasm_bindgen]
pub fn parse_components(datastring: &str) -> Result<JsValue, JsValue> {
    let comps: Components = datastring
        .parse::<Components>()
        .map_err(|e| e.to_string())?;
    let jscomps = JsValue::from_serde(&comps).map_err(|e| e.to_string())?;
    Ok(jscomps)
}

// Estructuras para gestión de factores de usuario
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
struct WFactorsCogenUserOptions {
    A_RED: Option<RenNrenCo2>,
    A_NEPB: Option<RenNrenCo2>,
}

#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
struct WFactorsRedUserOptions {
    RED1: Option<RenNrenCo2>,
    RED2: Option<RenNrenCo2>,
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
struct WFactorsUserOptions {
    cogen: Option<WFactorsCogenUserOptions>,
    red: Option<WFactorsRedUserOptions>,
    // strip_nepb: Option<bool>,
}

// Obtén factores a partir de la localización y los factores de usuario
//
// El campo de opciones tiene que ser al menos {}
#[wasm_bindgen]
pub fn new_wfactors(loc: &str, options: &JsValue) -> Result<JsValue, JsValue> {
    let defaults_wf = cte::CTE_USERWF;
    let rsoptions: WFactorsUserOptions = options.into_serde().map_err(|e| e.to_string())?;
    let red1 = rsoptions
        .red
        .and_then(|v| v.RED1)
        .or(Some(defaults_wf.red1));
    let red2 = rsoptions
        .red
        .and_then(|v| v.RED2)
        .or(Some(defaults_wf.red2));
    let cogen_to_grid = rsoptions.cogen.and_then(|v| v.A_RED).or(None);
    let cogen_to_nepb = rsoptions.cogen.and_then(|v| v.A_NEPB).or(None);
    let user_wf = cteepbd::UserWF {
        red1,
        red2,
        cogen_to_grid,
        cogen_to_nepb,
    };
    // Puede tener errores de parsing o de localidad
    let fp: Factors = cte::wfactors_from_loc(loc, &cte::CTE_LOCWF_RITE2014, user_wf, defaults_wf)
        .map_err(|e| e.to_string())?;
    let jsfactors = JsValue::from_serde(&fp).map_err(|e| e.to_string())?;
    Ok(jsfactors)
}

/// Calcula eficiencia energética
///
/// Calcula también la demanda renovable de ACS si es posible y agrega los datos al balance.
/// Si no se puede calcular esta demanda renovable balance.misc es un valor nulo (null en JS, None en Rust)
/// Si se puede agregar, se añade:
/// - balance.misc.demanda_anual_acs: demanda anual de ACS en kWh/a
/// - balance.misc.porcentaje_renovable_demanda_acs_nrb: fracción renovable de la demanda de ACS (kWh/kWh)
#[wasm_bindgen]
pub fn energy_performance(
    components: &JsValue,
    wfactors: &JsValue,
    kexp: f32,
    area: f32,
    dhw_needs: Option<f32>,
) -> Result<JsValue, JsValue> {
    let comps: Components = components.into_serde().map_err(|e| e.to_string())?;
    let wfacs: Factors = wfactors.into_serde().map_err(|e| e.to_string())?;
    let balance: Balance = cteepbd::energy_performance(&comps, &wfacs, kexp, area)
        .map(|b| cte::incorpora_demanda_renovable_acs_nrb(b, dhw_needs))
        .map_err(|e| e.to_string())?;
    let jsbalance = JsValue::from_serde(&balance).map_err(|e| e.to_string())?;
    Ok(jsbalance)
}
<|endoftext|>


<commit_before>//! Replaces 128-bit operators with lang item calls

use crate::prelude::*;

pub fn maybe_codegen<'a, 'tcx>(
    fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
    bin_op: BinOp,
    checked: bool,
    is_signed: bool,
    lhs: CValue<'tcx>,
    rhs: CValue<'tcx>,
    out_ty: Ty<'tcx>,
) -> Option<CValue<'tcx>> {
    if lhs.layout().ty != fx.tcx.types.u128 && lhs.layout().ty != fx.tcx.types.i128 {
        return None;
    }

    let lhs_val = lhs.load_scalar(fx);
    let rhs_val = rhs.load_scalar(fx);

    match bin_op {
        BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => {
            assert!(!checked);
            return None;
        }
        BinOp::Add | BinOp::Sub => {
            return None; // FIXME implement checked versions
        }
        BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
        BinOp::Mul => {
            let res = if checked {
                if is_signed {
                    fx.easy_call("__rust_i128_mulo", &[lhs, rhs], out_ty)
                } else {
                    fx.easy_call("__rust_u128_mulo", &[lhs, rhs], out_ty)
                }
            } else {
                let val_ty = if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
                fx.easy_call("__multi3", &[lhs, rhs], val_ty)
            };
            return Some(res);
        }
        BinOp::Div => {
            assert!(!checked);
            if is_signed {
                Some(fx.easy_call("__divti3", &[lhs, rhs], fx.tcx.types.i128))
            } else {
                Some(fx.easy_call("__udivti3", &[lhs, rhs], fx.tcx.types.u128))
            }
        }
        BinOp::Rem => {
            assert!(!checked);
            if is_signed {
                Some(fx.easy_call("__modti3", &[lhs, rhs], fx.tcx.types.i128))
            } else {
                Some(fx.easy_call("__umodti3", &[lhs, rhs], fx.tcx.types.u128))
            }
        }
        BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => {
            assert!(!checked);
            let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs_val);
            let (rhs_lsb, rhs_msb) = fx.bcx.ins().isplit(rhs_val);
            let res = match (bin_op, is_signed) {
                (BinOp::Eq, _) => {
                    let lsb_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_lsb, rhs_lsb);
                    let msb_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_msb, rhs_msb);
                    fx.bcx.ins().band(lsb_eq, msb_eq)
                }
                (BinOp::Ne, _) => {
                    let lsb_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_lsb, rhs_lsb);
                    let msb_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_msb, rhs_msb);
                    fx.bcx.ins().bor(lsb_ne, msb_ne)
                }
                _ => {
                    // FIXME implement it
                    let out_layout = fx.layout_of(out_ty);
                    return Some(crate::trap::trap_unreachable_ret_value(fx, out_layout, format!("unimplemented 128bit binop {:?}", bin_op)));
                },
            };

            let res = fx.bcx.ins().bint(types::I8, res);
            let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.bool));
            return Some(res);
        }
        BinOp::Shl | BinOp::Shr => {
            let is_overflow = if checked {
                // rhs >= 128

                // FIXME support non 128bit rhs
                /*let (rhs_lsb, rhs_msb) = fx.bcx.ins().isplit(rhs_val);
                let rhs_msb_gt_0 = fx.bcx.ins().icmp_imm(IntCC::NotEqual, rhs_msb, 0);
                let rhs_lsb_ge_128 = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, rhs_lsb, 127);
                let is_overflow = fx.bcx.ins().bor(rhs_msb_gt_0, rhs_lsb_ge_128);*/
                let is_overflow = fx.bcx.ins().bconst(types::B1, false);

                Some(fx.bcx.ins().bint(types::I8, is_overflow))
            } else {
                None
            };

            // Optimize `val >> 64`, because compiler_builtins uses it to deconstruct an 128bit
            // integer into its lsb and msb.
            // https://github.com/rust-lang-nursery/compiler-builtins/blob/79a6a1603d5672cbb9187ff41ff4d9b5048ac1cb/src/int/mod.rs#L217
            if let Some(64) = resolve_value_imm(fx.bcx.func, rhs_val) {
                let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs_val);
                let all_zeros = fx.bcx.ins().iconst(types::I64, 0);
                let val = match (bin_op, is_signed) {
                    (BinOp::Shr, false) => {
                        let val = fx.bcx.ins().iconcat(lhs_msb, all_zeros);
                        Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.u128)))
                    }
                    (BinOp::Shr, true) => {
                        let sign = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, lhs_msb, 0);
                        let all_ones = fx.bcx.ins().iconst(types::I64, u64::max_value() as i64);
                        let all_sign_bits = fx.bcx.ins().select(sign, all_zeros, all_ones);

                        let val = fx.bcx.ins().iconcat(lhs_msb, all_sign_bits);
                        Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.i128)))
                    }
                    (BinOp::Shl, _) => {
                        let val_ty = if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
                        let val = fx.bcx.ins().iconcat(all_zeros, lhs_lsb);
                        Some(CValue::by_val(val, fx.layout_of(val_ty)))
                    }
                    _ => None
                };
                if let Some(val) = val {
                    if let Some(is_overflow) = is_overflow {
                        let val = val.load_scalar(fx);
                        return Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty)))
                    } else {
                        return Some(val);
                    }
                }
            }

            let truncated_rhs = clif_intcast(fx, rhs_val, types::I32, false);
            let truncated_rhs = CValue::by_val(truncated_rhs, fx.layout_of(fx.tcx.types.u32));
            let val = match (bin_op, is_signed) {
                (BinOp::Shl, false) => {
                    fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.u128)
                }
                (BinOp::Shl, true) => {
                    fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.i128)
                }
                (BinOp::Shr, false) => {
                    fx.easy_call("__lshrti3", &[lhs, truncated_rhs], fx.tcx.types.u128)
                }
                (BinOp::Shr, true) => {
                    fx.easy_call("__ashrti3", &[lhs, truncated_rhs], fx.tcx.types.i128)
                }
                (_, _) => unreachable!(),
            };
            if let Some(is_overflow) = is_overflow {
                let val = val.load_scalar(fx);
                Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty)))
            } else {
                Some(val)
            }
        }
    }
}
<commit_msg>Implement 128bit comparison binops<commit_after>//! Replaces 128-bit operators with lang item calls

use crate::prelude::*;

pub fn maybe_codegen<'a, 'tcx>(
    fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
    bin_op: BinOp,
    checked: bool,
    is_signed: bool,
    lhs: CValue<'tcx>,
    rhs: CValue<'tcx>,
    out_ty: Ty<'tcx>,
) -> Option<CValue<'tcx>> {
    if lhs.layout().ty != fx.tcx.types.u128 && lhs.layout().ty != fx.tcx.types.i128 {
        return None;
    }

    let lhs_val = lhs.load_scalar(fx);
    let rhs_val = rhs.load_scalar(fx);

    match bin_op {
        BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => {
            assert!(!checked);
            return None;
        }
        BinOp::Add | BinOp::Sub => {
            return None; // FIXME implement checked versions
        }
        BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
        BinOp::Mul => {
            let res = if checked {
                if is_signed {
                    fx.easy_call("__rust_i128_mulo", &[lhs, rhs], out_ty)
                } else {
                    fx.easy_call("__rust_u128_mulo", &[lhs, rhs], out_ty)
                }
            } else {
                let val_ty = if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
                fx.easy_call("__multi3", &[lhs, rhs], val_ty)
            };
            return Some(res);
        }
        BinOp::Div => {
            assert!(!checked);
            if is_signed {
                Some(fx.easy_call("__divti3", &[lhs, rhs], fx.tcx.types.i128))
            } else {
                Some(fx.easy_call("__udivti3", &[lhs, rhs], fx.tcx.types.u128))
            }
        }
        BinOp::Rem => {
            assert!(!checked);
            if is_signed {
                Some(fx.easy_call("__modti3", &[lhs, rhs], fx.tcx.types.i128))
            } else {
                Some(fx.easy_call("__umodti3", &[lhs, rhs], fx.tcx.types.u128))
            }
        }
        BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => {
            assert!(!checked);
            let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs_val);
            let (rhs_lsb, rhs_msb) = fx.bcx.ins().isplit(rhs_val);

            let res = match bin_op {
                BinOp::Eq => {
                    let lsb_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_lsb, rhs_lsb);
                    let msb_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_msb, rhs_msb);
                    fx.bcx.ins().band(lsb_eq, msb_eq)
                }
                BinOp::Ne => {
                    let lsb_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_lsb, rhs_lsb);
                    let msb_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_msb, rhs_msb);
                    fx.bcx.ins().bor(lsb_ne, msb_ne)
                }
                _ => {
                    // if msb_eq {
                    //     lhs_cc
                    // } else {
                    //     msb_cc
                    // }
                    let cc = match (bin_op, is_signed) {
                        (BinOp::Ge, false) => IntCC::UnsignedGreaterThanOrEqual,
                        (BinOp::Gt, false) => IntCC::UnsignedGreaterThan,
                        (BinOp::Lt, false) => IntCC::UnsignedLessThan,
                        (BinOp::Le, false) => IntCC::UnsignedLessThanOrEqual,

                        (BinOp::Ge, true) => IntCC::SignedGreaterThanOrEqual,
                        (BinOp::Gt, true) => IntCC::SignedGreaterThan,
                        (BinOp::Lt, true) => IntCC::SignedLessThan,
                        (BinOp::Le, true) => IntCC::SignedLessThanOrEqual,
                        _ => unreachable!(),
                    };

                    let msb_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_msb, rhs_msb);
                    let lsb_cc = fx.bcx.ins().icmp(cc, lhs_lsb, rhs_lsb);
                    let msb_cc = fx.bcx.ins().icmp(cc, lhs_msb, rhs_msb);

                    fx.bcx.ins().select(msb_eq, lsb_cc, msb_cc)
                }
            };

            let res = fx.bcx.ins().bint(types::I8, res);
            let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.bool));
            return Some(res);
        }
        BinOp::Shl | BinOp::Shr => {
            let is_overflow = if checked {
                // rhs >= 128

                // FIXME support non 128bit rhs
                /*let (rhs_lsb, rhs_msb) = fx.bcx.ins().isplit(rhs_val);
                let rhs_msb_gt_0 = fx.bcx.ins().icmp_imm(IntCC::NotEqual, rhs_msb, 0);
                let rhs_lsb_ge_128 = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, rhs_lsb, 127);
                let is_overflow = fx.bcx.ins().bor(rhs_msb_gt_0, rhs_lsb_ge_128);*/
                let is_overflow = fx.bcx.ins().bconst(types::B1, false);

                Some(fx.bcx.ins().bint(types::I8, is_overflow))
            } else {
                None
            };

            // Optimize `val >> 64`, because compiler_builtins uses it to deconstruct an 128bit
            // integer into its lsb and msb.
            // https://github.com/rust-lang-nursery/compiler-builtins/blob/79a6a1603d5672cbb9187ff41ff4d9b5048ac1cb/src/int/mod.rs#L217
            if let Some(64) = resolve_value_imm(fx.bcx.func, rhs_val) {
                let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs_val);
                let all_zeros = fx.bcx.ins().iconst(types::I64, 0);
                let val = match (bin_op, is_signed) {
                    (BinOp::Shr, false) => {
                        let val = fx.bcx.ins().iconcat(lhs_msb, all_zeros);
                        Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.u128)))
                    }
                    (BinOp::Shr, true) => {
                        let sign = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, lhs_msb, 0);
                        let all_ones = fx.bcx.ins().iconst(types::I64, u64::max_value() as i64);
                        let all_sign_bits = fx.bcx.ins().select(sign, all_zeros, all_ones);

                        let val = fx.bcx.ins().iconcat(lhs_msb, all_sign_bits);
                        Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.i128)))
                    }
                    (BinOp::Shl, _) => {
                        let val_ty = if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
                        let val = fx.bcx.ins().iconcat(all_zeros, lhs_lsb);
                        Some(CValue::by_val(val, fx.layout_of(val_ty)))
                    }
                    _ => None
                };
                if let Some(val) = val {
                    if let Some(is_overflow) = is_overflow {
                        let val = val.load_scalar(fx);
                        return Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty)))
                    } else {
                        return Some(val);
                    }
                }
            }

            let truncated_rhs = clif_intcast(fx, rhs_val, types::I32, false);
            let truncated_rhs = CValue::by_val(truncated_rhs, fx.layout_of(fx.tcx.types.u32));
            let val = match (bin_op, is_signed) {
                (BinOp::Shl, false) => {
                    fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.u128)
                }
                (BinOp::Shl, true) => {
                    fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.i128)
                }
                (BinOp::Shr, false) => {
                    fx.easy_call("__lshrti3", &[lhs, truncated_rhs], fx.tcx.types.u128)
                }
                (BinOp::Shr, true) => {
                    fx.easy_call("__ashrti3", &[lhs, truncated_rhs], fx.tcx.types.i128)
                }
                (_, _) => unreachable!(),
            };
            if let Some(is_overflow) = is_overflow {
                let val = val.load_scalar(fx);
                Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty)))
            } else {
                Some(val)
            }
        }
    }
}
<|endoftext|>


<commit_before>//! This module implements the root command of the CLI tool.

use std::env;
use std::fmt;
use std::process;

use clap::{App, AppSettings, Arg, ArgMatches};
use failure::{bail, Error};
use log::{debug, info};

use crate::api::Api;
use crate::config::{prepare_environment, Auth, Config};
use crate::constants::{ARCH, PLATFORM, VERSION};
use crate::utils::system::{print_error, QuietExit};
use crate::utils::update::run_sentrycli_update_nagger;

const ABOUT: &str = "
Command line utility for Sentry.

This tool helps you manage remote resources on a Sentry server like
sourcemaps, debug symbols or releases.  Use `--help` on the subcommands
to learn more about them.";

macro_rules! each_subcommand {
    ($mac:ident) => {
        $mac!(upload_dif);
        $mac!(upload_dsym);
        $mac!(upload_proguard);
        $mac!(releases);
        $mac!(issues);
        $mac!(repos);
        $mac!(projects);
        $mac!(monitors);
        #[cfg(not(feature = "managed"))]
        $mac!(update);
        #[cfg(not(feature = "managed"))]
        $mac!(uninstall);
        $mac!(info);
        $mac!(login);
        $mac!(send_event);
        $mac!(react_native);
        $mac!(difutil);
        $mac!(bash_hook);

        // these here exist for legacy reasons only.  They were moved
        // to subcommands of the react-native command.  Note that
        // codepush was never available on that level.
        #[cfg(target_os = "macos")]
        $mac!(react_native_xcode);
        $mac!(react_native_gradle);
    };
}

// commands we want to run the update nagger on
const UPDATE_NAGGER_CMDS: &[&str] = &[
    "releases", "issues", "repos", "projects", "monitors", "info", "login", "difutil",
];

// it would be great if this could be a macro expansion as well
// but rust bug #37663 breaks location information then.
pub mod bash_hook;
pub mod info;
pub mod issues;
pub mod login;
pub mod monitors;
pub mod projects;
pub mod releases;
pub mod repos;
pub mod send_event;
#[cfg(not(feature = "managed"))]
pub mod uninstall;
#[cfg(not(feature = "managed"))]
pub mod update;
pub mod upload_dif;
pub mod upload_dsym;
pub mod upload_proguard;

pub mod react_native;
pub mod react_native_appcenter;
pub mod react_native_codepush;
pub mod react_native_gradle;
#[cfg(target_os = "macos")]
pub mod react_native_xcode;

pub mod difutil;
pub mod difutil_bundle_sources;
pub mod difutil_check;
pub mod difutil_find;
pub mod difutil_id;

fn preexecute_hooks() -> Result<bool, Error> {
    return sentry_react_native_xcode_wrap();

    #[cfg(target_os = "macos")]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        if let Ok(val) = env::var("__SENTRY_RN_WRAP_XCODE_CALL") {
            env::remove_var("__SENTRY_RN_WRAP_XCODE_CALL");
            if &val == "1" {
                react_native_xcode::wrap_call()?;
                return Ok(true);
            }
        }
        Ok(false)
    }

    #[cfg(not(target_os = "macos"))]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        Ok(false)
    }
}

fn configure_args(config: &mut Config, matches: &ArgMatches<'_>) -> Result<(), Error> {
    if let Some(url) = matches.value_of("url") {
        config.set_base_url(url);
    }

    if let Some(api_key) = matches.value_of("api_key") {
        config.set_auth(Auth::Key(api_key.to_owned()));
    }

    if let Some(auth_token) = matches.value_of("auth_token") {
        config.set_auth(Auth::Token(auth_token.to_owned()));
    }

    if let Some(level_str) = matches.value_of("log_level") {
        match level_str.parse() {
            Ok(level) => {
                config.set_log_level(level);
            }
            Err(_) => {
                bail!("Unknown log level: {}", level_str);
            }
        }
    }

    Ok(())
}

fn add_commands<'a, 'b>(mut app: App<'a, 'b>) -> App<'a, 'b> {
    macro_rules! add_subcommand {
        ($name:ident) => {{
            let mut cmd = $name::make_app(App::new(stringify!($name).replace("_", "-").as_str()));

            // for legacy reasons
            if stringify!($name).starts_with("react_native_") {
                cmd = cmd.setting(AppSettings::Hidden);
            }

            app = app.subcommand(cmd);
        }};
    }

    each_subcommand!(add_subcommand);
    app
}

#[allow(clippy::cognitive_complexity)]
fn run_command(matches: &ArgMatches<'_>) -> Result<(), Error> {
    macro_rules! execute_subcommand {
        ($name:ident) => {{
            let cmd = stringify!($name).replace("_", "-");
            if let Some(sub_matches) = matches.subcommand_matches(&cmd) {
                let rv = $name::execute(&sub_matches)?;
                if UPDATE_NAGGER_CMDS.iter().any(|x| x == &cmd) {
                    run_sentrycli_update_nagger();
                }
                return Ok(rv);
            }
        }};
    }

    each_subcommand!(execute_subcommand);
    unreachable!();
}

struct DebugArgs<'a>(Vec<&'a str>);

impl<'a> fmt::Display for DebugArgs<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (idx, arg) in self.0.iter().enumerate() {
            if idx > 0 {
                write!(f, " ")?;
            }
            write!(f, "{:?}", arg)?;
        }
        Ok(())
    }
}

/// Given an argument vector and a `Config` this executes the
/// command line and returns the result.
pub fn execute(args: &[String]) -> Result<(), Error> {
    let mut config = Config::from_cli_config()?;

    // special case for the xcode integration for react native.  For more
    // information see commands/react_native_xcode.rs
    if preexecute_hooks()? {
        return Ok(());
    }

    let mut app = App::new("sentry-cli")
        .help_message("Print this help message.")
        .version(VERSION)
        .version_message("Print version information.")
        .about(ABOUT)
        .max_term_width(100)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .global_setting(AppSettings::UnifiedHelpMessage)
        .arg(Arg::with_name("url").value_name("URL").long("url").help(
            "Fully qualified URL to the Sentry server.{n}\
             [defaults to https://sentry.io/]",
        ))
        .arg(
            Arg::with_name("auth_token")
                .value_name("AUTH_TOKEN")
                .long("auth-token")
                .help("Use the given Sentry auth token."),
        )
        .arg(
            Arg::with_name("api_key")
                .value_name("API_KEY")
                .long("api-key")
                .help("The given Sentry API key."),
        )
        .arg(
            Arg::with_name("log_level")
                .value_name("LOG_LEVEL")
                .long("log-level")
                .global(true)
                .help(
                    "Set the log output verbosity.{n}\
                     [valid levels: TRACE, DEBUG, INFO, WARN, ERROR]",
                ),
        );

    app = add_commands(app);
    let matches = app.get_matches_from_safe(&args[..])?;
    configure_args(&mut config, &matches)?;

    // bind the config to the process and fetch an immutable reference to it
    config.bind_to_process();
    if Config::current().get_filename().exists() {
        info!(
            "Loaded config from {}",
            Config::current().get_filename().display()
        );
    }

    debug!(
        "sentry-cli version: {}, platform: \"{}\", architecture: \"{}\"",
        VERSION, PLATFORM, ARCH
    );

    info!(
        "sentry-cli was invoked with the following command line: {}",
        DebugArgs(args.iter().map(String::as_str).collect())
    );

    run_command(&matches)
}

fn run() -> Result<(), Error> {
    prepare_environment();
    match execute(&env::args().collect::<Vec<String>>()) {
        Ok(()) => Ok(()),
        Err(err) => {
            // if the user hit an error, it might be time to run the update
            // nagger because maybe they tried to do something only newer
            // versions support.
            debug!("error: running update nagger");
            run_sentrycli_update_nagger();
            Err(err)
        }
    }
}

fn setup() {
    use crate::utils::logging::Logger;

    crate::utils::system::init_backtrace();
    crate::utils::system::load_dotenv();

    // we use debug internally but our log handler then rejects to a lower limit.
    // This is okay for our uses but not as efficient.
    log::set_max_level(log::LevelFilter::Debug);

    // if we work with crash reporting we initialize the sentry system.  This
    // also configures the logger.
    #[cfg(feature = "with_crash_reporting")]
    {
        crate::utils::crashreporting::setup(Box::new(Logger));
    }
    #[cfg(not(feature = "with_crash_reporting"))]
    {
        static LOGGER: Logger = Logger;
        log::set_logger(&Logger);
    }
}

/// Executes the command line application and exits the process.
pub fn main() {
    setup();
    let result = run();

    let status_code = match result {
        Ok(()) => 0,
        Err(err) => {
            if let Some(&QuietExit(code)) = err.downcast_ref() {
                code
            } else {
                print_error(&err);
                #[cfg(feature = "with_crash_reporting")]
                {
                    crate::utils::crashreporting::try_report_to_sentry(&err);
                }
                1
            }
        }
    };

    // before we shut down we unbind the api to give the connection pool
    // a chance to collect.  Not doing so has shown to cause hung threads
    // on windows.
    Api::dispose_pool();
    process::exit(status_code);
}
<commit_msg>fix: Dont panic on unknown log level (#733)<commit_after>//! This module implements the root command of the CLI tool.

use std::env;
use std::fmt;
use std::process;

use clap::{App, AppSettings, Arg, ArgMatches};
use failure::{bail, Error};
use log::{debug, info};

use crate::api::Api;
use crate::config::{prepare_environment, Auth, Config};
use crate::constants::{ARCH, PLATFORM, VERSION};
use crate::utils::system::{print_error, QuietExit};
use crate::utils::update::run_sentrycli_update_nagger;

const ABOUT: &str = "
Command line utility for Sentry.

This tool helps you manage remote resources on a Sentry server like
sourcemaps, debug symbols or releases.  Use `--help` on the subcommands
to learn more about them.";

macro_rules! each_subcommand {
    ($mac:ident) => {
        $mac!(upload_dif);
        $mac!(upload_dsym);
        $mac!(upload_proguard);
        $mac!(releases);
        $mac!(issues);
        $mac!(repos);
        $mac!(projects);
        $mac!(monitors);
        #[cfg(not(feature = "managed"))]
        $mac!(update);
        #[cfg(not(feature = "managed"))]
        $mac!(uninstall);
        $mac!(info);
        $mac!(login);
        $mac!(send_event);
        $mac!(react_native);
        $mac!(difutil);
        $mac!(bash_hook);

        // these here exist for legacy reasons only.  They were moved
        // to subcommands of the react-native command.  Note that
        // codepush was never available on that level.
        #[cfg(target_os = "macos")]
        $mac!(react_native_xcode);
        $mac!(react_native_gradle);
    };
}

// commands we want to run the update nagger on
const UPDATE_NAGGER_CMDS: &[&str] = &[
    "releases", "issues", "repos", "projects", "monitors", "info", "login", "difutil",
];

// it would be great if this could be a macro expansion as well
// but rust bug #37663 breaks location information then.
pub mod bash_hook;
pub mod info;
pub mod issues;
pub mod login;
pub mod monitors;
pub mod projects;
pub mod releases;
pub mod repos;
pub mod send_event;
#[cfg(not(feature = "managed"))]
pub mod uninstall;
#[cfg(not(feature = "managed"))]
pub mod update;
pub mod upload_dif;
pub mod upload_dsym;
pub mod upload_proguard;

pub mod react_native;
pub mod react_native_appcenter;
pub mod react_native_codepush;
pub mod react_native_gradle;
#[cfg(target_os = "macos")]
pub mod react_native_xcode;

pub mod difutil;
pub mod difutil_bundle_sources;
pub mod difutil_check;
pub mod difutil_find;
pub mod difutil_id;

fn preexecute_hooks() -> Result<bool, Error> {
    return sentry_react_native_xcode_wrap();

    #[cfg(target_os = "macos")]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        if let Ok(val) = env::var("__SENTRY_RN_WRAP_XCODE_CALL") {
            env::remove_var("__SENTRY_RN_WRAP_XCODE_CALL");
            if &val == "1" {
                react_native_xcode::wrap_call()?;
                return Ok(true);
            }
        }
        Ok(false)
    }

    #[cfg(not(target_os = "macos"))]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        Ok(false)
    }
}

fn configure_args(config: &mut Config, matches: &ArgMatches<'_>) -> Result<(), Error> {
    if let Some(url) = matches.value_of("url") {
        config.set_base_url(url);
    }

    if let Some(api_key) = matches.value_of("api_key") {
        config.set_auth(Auth::Key(api_key.to_owned()));
    }

    if let Some(auth_token) = matches.value_of("auth_token") {
        config.set_auth(Auth::Token(auth_token.to_owned()));
    }

    if let Some(level_str) = matches.value_of("log_level") {
        match level_str.parse() {
            Ok(level) => {
                config.set_log_level(level);
            }
            Err(_) => {
                bail!("Unknown log level: {}", level_str);
            }
        }
    }

    Ok(())
}

fn add_commands<'a, 'b>(mut app: App<'a, 'b>) -> App<'a, 'b> {
    macro_rules! add_subcommand {
        ($name:ident) => {{
            let mut cmd = $name::make_app(App::new(stringify!($name).replace("_", "-").as_str()));

            // for legacy reasons
            if stringify!($name).starts_with("react_native_") {
                cmd = cmd.setting(AppSettings::Hidden);
            }

            app = app.subcommand(cmd);
        }};
    }

    each_subcommand!(add_subcommand);
    app
}

#[allow(clippy::cognitive_complexity)]
fn run_command(matches: &ArgMatches<'_>) -> Result<(), Error> {
    macro_rules! execute_subcommand {
        ($name:ident) => {{
            let cmd = stringify!($name).replace("_", "-");
            if let Some(sub_matches) = matches.subcommand_matches(&cmd) {
                let rv = $name::execute(&sub_matches)?;
                if UPDATE_NAGGER_CMDS.iter().any(|x| x == &cmd) {
                    run_sentrycli_update_nagger();
                }
                return Ok(rv);
            }
        }};
    }

    each_subcommand!(execute_subcommand);
    unreachable!();
}

struct DebugArgs<'a>(Vec<&'a str>);

impl<'a> fmt::Display for DebugArgs<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (idx, arg) in self.0.iter().enumerate() {
            if idx > 0 {
                write!(f, " ")?;
            }
            write!(f, "{:?}", arg)?;
        }
        Ok(())
    }
}

/// Given an argument vector and a `Config` this executes the
/// command line and returns the result.
pub fn execute(args: &[String]) -> Result<(), Error> {
    let mut config = Config::from_cli_config()?;

    // special case for the xcode integration for react native.  For more
    // information see commands/react_native_xcode.rs
    if preexecute_hooks()? {
        return Ok(());
    }

    let mut app = App::new("sentry-cli")
        .help_message("Print this help message.")
        .version(VERSION)
        .version_message("Print version information.")
        .about(ABOUT)
        .max_term_width(100)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .global_setting(AppSettings::UnifiedHelpMessage)
        .arg(Arg::with_name("url").value_name("URL").long("url").help(
            "Fully qualified URL to the Sentry server.{n}\
             [defaults to https://sentry.io/]",
        ))
        .arg(
            Arg::with_name("auth_token")
                .value_name("AUTH_TOKEN")
                .long("auth-token")
                .help("Use the given Sentry auth token."),
        )
        .arg(
            Arg::with_name("api_key")
                .value_name("API_KEY")
                .long("api-key")
                .help("The given Sentry API key."),
        )
        .arg(
            Arg::with_name("log_level")
                .value_name("LOG_LEVEL")
                .long("log-level")
                .possible_values(&["trace", "debug", "info", "warn", "error"])
                .global(true)
                .help("Set the log output verbosity."),
        );

    app = add_commands(app);
    let matches = app.get_matches_from_safe(&args[..])?;
    configure_args(&mut config, &matches)?;

    // bind the config to the process and fetch an immutable reference to it
    config.bind_to_process();
    if Config::current().get_filename().exists() {
        info!(
            "Loaded config from {}",
            Config::current().get_filename().display()
        );
    }

    debug!(
        "sentry-cli version: {}, platform: \"{}\", architecture: \"{}\"",
        VERSION, PLATFORM, ARCH
    );

    info!(
        "sentry-cli was invoked with the following command line: {}",
        DebugArgs(args.iter().map(String::as_str).collect())
    );

    run_command(&matches)
}

fn run() -> Result<(), Error> {
    prepare_environment();
    match execute(&env::args().collect::<Vec<String>>()) {
        Ok(()) => Ok(()),
        Err(err) => {
            // if the user hit an error, it might be time to run the update
            // nagger because maybe they tried to do something only newer
            // versions support.
            debug!("error: running update nagger");
            run_sentrycli_update_nagger();
            Err(err)
        }
    }
}

fn setup() {
    use crate::utils::logging::Logger;

    crate::utils::system::init_backtrace();
    crate::utils::system::load_dotenv();

    // we use debug internally but our log handler then rejects to a lower limit.
    // This is okay for our uses but not as efficient.
    log::set_max_level(log::LevelFilter::Debug);

    // if we work with crash reporting we initialize the sentry system.  This
    // also configures the logger.
    #[cfg(feature = "with_crash_reporting")]
    {
        crate::utils::crashreporting::setup(Box::new(Logger));
    }
    #[cfg(not(feature = "with_crash_reporting"))]
    {
        static LOGGER: Logger = Logger;
        log::set_logger(&Logger);
    }
}

/// Executes the command line application and exits the process.
pub fn main() {
    setup();
    let result = run();

    let status_code = match result {
        Ok(()) => 0,
        Err(err) => {
            if let Some(&QuietExit(code)) = err.downcast_ref() {
                code
            } else {
                print_error(&err);
                #[cfg(feature = "with_crash_reporting")]
                {
                    crate::utils::crashreporting::try_report_to_sentry(&err);
                }
                1
            }
        }
    };

    // before we shut down we unbind the api to give the connection pool
    // a chance to collect.  Not doing so has shown to cause hung threads
    // on windows.
    Api::dispose_pool();
    process::exit(status_code);
}
<|endoftext|>


<commit_before>use std::thread;
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use chess_move::*;
use tt::*;
use position::Position;

pub enum Command {
    Search {
        search_id: usize,
        position: Position,
        depth: u8,
        lower_bound: Value,
        upper_bound: Value,
    },
    Stop,
    Exit,
}

pub enum Report {
    Progress {
        search_id: usize,
        searched_nodes: NodeCount,
        depth: u8,
    },
    Done {
        search_id: usize,
        searched_nodes: NodeCount,
        value: Option<Value>,
    },
}

pub fn run_deepening(tt: Arc<TranspositionTable>,
                     commands: Receiver<Command>,
                     reports: Sender<Report>) {
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        run(tt, slave_commands_rx, slave_reports_tx);
    });
    let mut pending_command = None;
    loop {
        // If there is a pending command, we take it, otherwise we
        // block and wait to receive a new one.
        let command = match pending_command.take() {
            Some(cmd) => cmd,
            None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
        };

        match command {
            Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                let mut searched_nodes_final = 0;
                let mut value_final = None;
                'depthloop: for n in 1..(depth + 1) {
                    slave_commands_tx.send(Command::Search {
                                         search_id: n as usize,
                                         position: position.clone(),
                                         depth: n,
                                         lower_bound: lower_bound,
                                         upper_bound: upper_bound,
                                     })
                                     .unwrap();
                    loop {
                        match slave_reports_rx.recv().unwrap() {
                            Report::Progress { searched_nodes, .. } => {
                                reports.send(Report::Progress {
                                           search_id: search_id,
                                           searched_nodes: searched_nodes_final + searched_nodes,
                                           depth: n,
                                       })
                                       .ok();
                                if pending_command.is_none() {
                                    pending_command = match commands.try_recv() {
                                        Ok(cmd) => {
                                            slave_commands_tx.send(Command::Stop).unwrap();
                                            Some(cmd)
                                        }
                                        _ => None,
                                    }
                                }
                            }
                            Report::Done { searched_nodes, value, .. } => {
                                searched_nodes_final += searched_nodes;
                                if n == depth {
                                    value_final = value;
                                }
                                if pending_command.is_some() {
                                    break 'depthloop;
                                }
                                break;
                            }
                        }
                    }
                }
                reports.send(Report::Done {
                           search_id: search_id,
                           searched_nodes: searched_nodes_final,
                           value: value_final,
                       })
                       .ok();
            }
            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }
            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}

pub fn run(tt: Arc<TranspositionTable>, commands: Receiver<Command>, reports: Sender<Report>) {
    thread_local!(
        static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
    );
    MOVE_STACK.with(|s| {
        let mut move_stack = unsafe { &mut *s.get() };
        let mut pending_command = None;
        loop {
            // If there is a pending command, we take it, otherwise we
            // block and wait to receive a new one.
            let command = match pending_command.take() {
                Some(cmd) => cmd,
                None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
            };

            match command {
                Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                    let mut report = |n| {
                        reports.send(Report::Progress {
                                   search_id: search_id,
                                   searched_nodes: n,
                                   depth: depth,
                               })
                               .ok();
                        match commands.try_recv() {
                            Ok(x) => {
                                pending_command = Some(x);
                                Err(TerminatedSearch)
                            }
                            _ => Ok(()),
                        }
                    };
                    let mut search = Search::new(position, &tt, move_stack, &mut report);
                    let value = search.run(lower_bound, upper_bound, depth).ok();
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               depth: depth,
                           })
                           .ok();
                    reports.send(Report::Done {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               value: value,
                           })
                           .ok();
                    search.reset();
                }
                Command::Stop => continue,
                Command::Exit => break,
            }
        }
    })
}

/// Represents a terminated search condition.
pub struct TerminatedSearch;

enum NodePhase {
    Pristine,
    TriedHashMove,
    GeneratedMoves,
}

struct NodeState {
    phase: NodePhase,
    entry: EntryData,
}

const NODE_COUNT_REPORT_INTERVAL: NodeCount = 10000;

pub struct Search<'a> {
    tt: &'a TranspositionTable,
    position: Position,
    moves: &'a mut MoveStack,
    moves_starting_ply: usize,
    state_stack: Vec<NodeState>,
    reported_nodes: NodeCount,
    unreported_nodes: NodeCount,
    report_function: &'a mut FnMut(NodeCount) -> Result<(), TerminatedSearch>,
}

impl<'a> Search<'a> {
    /// Creates a new instance.
    ///
    /// `report_function` should be a function that registers the
    /// search progress. It will be called with the number of searched
    /// positions from the beginning of the search to this moment.
    pub fn new(p: Position,
               tt: &'a TranspositionTable,
               moves: &'a mut MoveStack,
               report_function: &'a mut FnMut(NodeCount) -> Result<(), TerminatedSearch>)
               -> Search<'a> {
        let moves_starting_ply = moves.ply();
        Search {
            tt: tt,
            position: p,
            moves: moves,
            moves_starting_ply: moves_starting_ply,
            state_stack: Vec::with_capacity(32),
            reported_nodes: 0,
            unreported_nodes: 0,
            report_function: report_function,
        }
    }

    /// Performs a principal variation search and returns a result.
    ///
    /// **Important note**: This method may leave un-restored move
    /// lists in the move stack. Call `reset` if you want the move
    /// stack to be restored to the state it had when the search
    /// instance was created.
    pub fn run(&mut self,
               mut alpha: Value, // lower bound
               beta: Value, // upper bound
               depth: u8)
               -> Result<Value, TerminatedSearch> {
        assert!(alpha < beta);

        let entry = self.node_begin();

        // Check if the TT entry gives the result.
        if entry.depth() >= depth {
            let value = entry.value();
            let bound = entry.bound();
            if (value >= beta && bound == BOUND_LOWER) ||
               (value <= alpha && bound == BOUND_UPPER) || (bound == BOUND_EXACT) {
                self.node_end();
                return Ok(value);
            }
        }

        // Initial guests for the final result.
        let mut bound = BOUND_UPPER;
        let mut best_move = Move::invalid();

        if depth == 0 {
            // On leaf nodes, do quiescence search.
            let (value, nodes) = self.position
                                     .evaluate_quiescence(alpha, beta, Some(entry.eval_value()));
            try!(self.report_progress(nodes));

            // See how good this position is.
            if value >= beta {
                alpha = beta;
                bound = BOUND_LOWER;
            } else if value > alpha {
                alpha = value;
                bound = BOUND_EXACT;
            }

        } else {
            // On non-leaf nodes, try moves.
            let mut no_moves_yet = true;
            while let Some(m) = self.do_move() {
                try!(self.report_progress(1));

                // Make a recursive call.
                let value = if no_moves_yet {
                    // The first move we analyze with a fully open window
                    // (alpha, beta). If this happens to be a good move,
                    // it will probably raise `alpha`.
                    no_moves_yet = false;
                    -try!(self.run(-beta, -alpha, depth - 1))
                } else {
                    // For the next moves we first try to prove that they
                    // are not better than our current best move. For this
                    // purpose we analyze them with a null window (alpha,
                    // alpha + 1). This is faster than a full window
                    // search. Only if we are certain that the move is
                    // better than our current best move, we do a
                    // full-window search.
                    match -try!(self.run(-alpha - 1, -alpha, depth - 1)) {
                        x if x <= alpha => x,
                        _ => -try!(self.run(-beta, -alpha, depth - 1)),
                    }
                };
                self.undo_move();

                // See how good this move was.
                if value >= beta {
                    // This move is so good, that the opponent will
                    // probably not allow this line of play to
                    // happen. Therefore we should not lose any more time
                    // on this position.
                    alpha = beta;
                    bound = BOUND_LOWER;
                    best_move = m;
                    break;
                }
                if value > alpha {
                    // We found a new best move.
                    alpha = value;
                    bound = BOUND_EXACT;
                    best_move = m;
                }
            }

            // Check if we are in a final position (no legal moves).
            if no_moves_yet {
                alpha = self.position.evaluate_final();
                bound = BOUND_EXACT;
            }
        }

        self.store(alpha, bound, depth, best_move);
        self.node_end();
        Ok(alpha)
    }

    /// Returns the number of searched positions.
    #[inline(always)]
    pub fn node_count(&self) -> NodeCount {
        self.reported_nodes + self.unreported_nodes
    }

    /// Resets the instance to the state it had when it was created.
    #[inline]
    pub fn reset(&mut self) {
        while self.moves.ply() > self.moves_starting_ply {
            self.moves.restore();
        }
        self.state_stack.clear();
        self.reported_nodes = 0;
        self.unreported_nodes = 0;
    }

    // Declares that we are starting to process a new node.
    //
    // Each recursive call to `run` begins with a call to
    // `node_begin`. The returned value is a TT entry telling
    // everything we know about the current position.
    #[inline]
    fn node_begin(&mut self) -> EntryData {
        // Consult the transposition table.
        let entry = if let Some(e) = self.tt.probe(self.position.hash()) {
            e
        } else {
            EntryData::new(0, BOUND_NONE, 0, 0, self.position.evaluate_static())
        };
        self.state_stack.push(NodeState {
            phase: NodePhase::Pristine,
            entry: entry,
        });
        entry
    }

    // Declares that we are done processing the current node.
    //
    // Each recursive call to `run` ends with a call to `node_end`.
    #[inline]
    fn node_end(&mut self) {
        if let NodePhase::Pristine = self.state_stack.last().unwrap().phase {
            // For pristine nodes we have not saved the move list
            // yet, so we should not restore it.
        } else {
            self.moves.restore();
        }
        self.state_stack.pop();
    }

    // Plays the next legal move in the current position and returns
    // it.
    //
    // Each call to `do_move` for the same position will play and
    // return a different move. When all legal moves has been played,
    // `None` will be returned. `do_move` will do whatever it can to
    // play the best moves first, and the worst last. It will also try
    // to be efficient, for example it will generate the list of all
    // pseudo-legal moves at the last possible moment.
    #[inline]
    fn do_move(&mut self) -> Option<Move> {
        let state = self.state_stack.last_mut().unwrap();

        if let NodePhase::Pristine = state.phase {
            // We save the move list at the last possible moment,
            // because most of the nodes are leafs.
            self.moves.save();

            // We always try the hash move first.
            state.phase = NodePhase::TriedHashMove;
            if state.entry.move16() != 0 {
                if let Some(m) = self.position.try_move_digest(state.entry.move16()) {
                    if self.position.do_move(m) {
                        return Some(m);
                    }
                }
            }
        }

        if let NodePhase::TriedHashMove = state.phase {
            // After the hash move, we generate all pseudo-legal
            // moves. But we should not forget to remove the already
            // tried hash move from the list.
            self.position.generate_moves(self.moves);
            if state.entry.move16() != 0 {
                self.moves.remove_move(state.entry.move16());
            }
            state.phase = NodePhase::GeneratedMoves;
        }

        // For the last, we spit the generated moves out.
        while let Some(m) = self.moves.remove_best_move() {
            if self.position.do_move(m) {
                return Some(m);
            }
        }
        None
    }

    // Takes the last played move back.
    #[inline]
    fn undo_move(&mut self) {
        self.position.undo_move();
    }

    // Stores updated node information in the transposition table.
    #[inline]
    fn store(&mut self, value: Value, bound: BoundType, depth: u8, best_move: Move) {
        let entry = &self.state_stack.last().unwrap().entry;
        let move16 = match best_move.digest() {
            0 => entry.move16(),
            x => x,
        };
        self.tt.store(self.position.hash(),
                      EntryData::new(value, bound, depth, move16, entry.eval_value()));
    }

    // Reports search progress.
    //
    // From time to time, we should report how many nodes had been
    // searched since the beginning of the search. This also gives an
    // opportunity for the search to be terminated.
    #[inline]
    fn report_progress(&mut self, new_nodes: NodeCount) -> Result<(), TerminatedSearch> {
        self.unreported_nodes += new_nodes;
        if self.unreported_nodes > NODE_COUNT_REPORT_INTERVAL {
            self.reported_nodes += self.unreported_nodes;
            self.unreported_nodes = 0;
            try!((*self.report_function)(self.reported_nodes));
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::Search;
    use chess_move::*;
    use tt::*;
    use position::Position;

    #[test]
    fn test_search() {
        let p = Position::from_fen("8/8/8/8/3q3k/7n/6PP/2Q2R1K b - - 0 1").ok().unwrap();
        let tt = TranspositionTable::new();
        let mut moves = MoveStack::new();
        let mut report = |_| Ok(());
        let mut search = Search::new(p, &tt, &mut moves, &mut report);
        let value = search.run(-30000, 30000, 2)
                          .ok()
                          .unwrap();
        assert!(value < -300);
        search.reset();
        let value = search.run(-30000, 30000, 4)
                          .ok()
                          .unwrap();
        assert!(value >= 20000);
    }
}
<commit_msg>make report_function return bool<commit_after>use std::thread;
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use chess_move::*;
use tt::*;
use position::Position;

pub enum Command {
    Search {
        search_id: usize,
        position: Position,
        depth: u8,
        lower_bound: Value,
        upper_bound: Value,
    },
    Stop,
    Exit,
}

pub enum Report {
    Progress {
        search_id: usize,
        searched_nodes: NodeCount,
        depth: u8,
    },
    Done {
        search_id: usize,
        searched_nodes: NodeCount,
        value: Option<Value>,
    },
}

pub fn run_deepening(tt: Arc<TranspositionTable>,
                     commands: Receiver<Command>,
                     reports: Sender<Report>) {
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        run(tt, slave_commands_rx, slave_reports_tx);
    });
    let mut pending_command = None;
    loop {
        // If there is a pending command, we take it, otherwise we
        // block and wait to receive a new one.
        let command = match pending_command.take() {
            Some(cmd) => cmd,
            None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
        };

        match command {
            Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                let mut searched_nodes_final = 0;
                let mut value_final = None;
                'depthloop: for n in 1..(depth + 1) {
                    slave_commands_tx.send(Command::Search {
                                         search_id: n as usize,
                                         position: position.clone(),
                                         depth: n,
                                         lower_bound: lower_bound,
                                         upper_bound: upper_bound,
                                     })
                                     .unwrap();
                    loop {
                        match slave_reports_rx.recv().unwrap() {
                            Report::Progress { searched_nodes, .. } => {
                                reports.send(Report::Progress {
                                           search_id: search_id,
                                           searched_nodes: searched_nodes_final + searched_nodes,
                                           depth: n,
                                       })
                                       .ok();
                                if pending_command.is_none() {
                                    pending_command = match commands.try_recv() {
                                        Ok(cmd) => {
                                            slave_commands_tx.send(Command::Stop).unwrap();
                                            Some(cmd)
                                        }
                                        _ => None,
                                    }
                                }
                            }
                            Report::Done { searched_nodes, value, .. } => {
                                searched_nodes_final += searched_nodes;
                                if n == depth {
                                    value_final = value;
                                }
                                if pending_command.is_some() {
                                    break 'depthloop;
                                }
                                break;
                            }
                        }
                    }
                }
                reports.send(Report::Done {
                           search_id: search_id,
                           searched_nodes: searched_nodes_final,
                           value: value_final,
                       })
                       .ok();
            }
            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }
            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}

pub fn run(tt: Arc<TranspositionTable>, commands: Receiver<Command>, reports: Sender<Report>) {
    thread_local!(
        static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
    );
    MOVE_STACK.with(|s| {
        let mut move_stack = unsafe { &mut *s.get() };
        let mut pending_command = None;
        loop {
            // If there is a pending command, we take it, otherwise we
            // block and wait to receive a new one.
            let command = match pending_command.take() {
                Some(cmd) => cmd,
                None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
            };

            match command {
                Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                    let mut report = |n| {
                        reports.send(Report::Progress {
                                   search_id: search_id,
                                   searched_nodes: n,
                                   depth: depth,
                               })
                               .ok();
                        if let Ok(cmd) = commands.try_recv() {
                            pending_command = Some(cmd);
                            true
                        } else {
                            false
                        }
                    };
                    let mut search = Search::new(position, &tt, move_stack, &mut report);
                    let value = search.run(lower_bound, upper_bound, depth).ok();
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               depth: depth,
                           })
                           .ok();
                    reports.send(Report::Done {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               value: value,
                           })
                           .ok();
                    search.reset();
                }
                Command::Stop => continue,
                Command::Exit => break,
            }
        }
    })
}

/// Represents a terminated search condition.
pub struct TerminatedSearch;

enum NodePhase {
    Pristine,
    TriedHashMove,
    GeneratedMoves,
}

struct NodeState {
    phase: NodePhase,
    entry: EntryData,
}

const NODE_COUNT_REPORT_INTERVAL: NodeCount = 10000;

pub struct Search<'a> {
    tt: &'a TranspositionTable,
    position: Position,
    moves: &'a mut MoveStack,
    moves_starting_ply: usize,
    state_stack: Vec<NodeState>,
    reported_nodes: NodeCount,
    unreported_nodes: NodeCount,
    report_function: &'a mut FnMut(NodeCount) -> bool,
}

impl<'a> Search<'a> {
    /// Creates a new instance.
    ///
    /// `report_function` should be a function that registers the
    /// search progress. It will be called with the number of searched
    /// positions from the beginning of the search to this moment. The
    /// function should return `true` if the search should be
    /// terminated, otherwise it should return `false`.
    pub fn new(p: Position,
               tt: &'a TranspositionTable,
               moves: &'a mut MoveStack,
               report_function: &'a mut FnMut(NodeCount) -> bool)
               -> Search<'a> {
        let moves_starting_ply = moves.ply();
        Search {
            tt: tt,
            position: p,
            moves: moves,
            moves_starting_ply: moves_starting_ply,
            state_stack: Vec::with_capacity(32),
            reported_nodes: 0,
            unreported_nodes: 0,
            report_function: report_function,
        }
    }

    /// Performs a principal variation search and returns a result.
    ///
    /// **Important note**: This method may leave un-restored move
    /// lists in the move stack. Call `reset` if you want the move
    /// stack to be restored to the state it had when the search
    /// instance was created.
    pub fn run(&mut self,
               mut alpha: Value, // lower bound
               beta: Value, // upper bound
               depth: u8)
               -> Result<Value, TerminatedSearch> {
        assert!(alpha < beta);

        let entry = self.node_begin();

        // Check if the TT entry gives the result.
        if entry.depth() >= depth {
            let value = entry.value();
            let bound = entry.bound();
            if (value >= beta && bound == BOUND_LOWER) ||
               (value <= alpha && bound == BOUND_UPPER) || (bound == BOUND_EXACT) {
                self.node_end();
                return Ok(value);
            }
        }

        // Initial guests for the final result.
        let mut bound = BOUND_UPPER;
        let mut best_move = Move::invalid();

        if depth == 0 {
            // On leaf nodes, do quiescence search.
            let (value, nodes) = self.position
                                     .evaluate_quiescence(alpha, beta, Some(entry.eval_value()));
            try!(self.report_progress(nodes));

            // See how good this position is.
            if value >= beta {
                alpha = beta;
                bound = BOUND_LOWER;
            } else if value > alpha {
                alpha = value;
                bound = BOUND_EXACT;
            }

        } else {
            // On non-leaf nodes, try moves.
            let mut no_moves_yet = true;
            while let Some(m) = self.do_move() {
                try!(self.report_progress(1));

                // Make a recursive call.
                let value = if no_moves_yet {
                    // The first move we analyze with a fully open window
                    // (alpha, beta). If this happens to be a good move,
                    // it will probably raise `alpha`.
                    no_moves_yet = false;
                    -try!(self.run(-beta, -alpha, depth - 1))
                } else {
                    // For the next moves we first try to prove that they
                    // are not better than our current best move. For this
                    // purpose we analyze them with a null window (alpha,
                    // alpha + 1). This is faster than a full window
                    // search. Only if we are certain that the move is
                    // better than our current best move, we do a
                    // full-window search.
                    match -try!(self.run(-alpha - 1, -alpha, depth - 1)) {
                        x if x <= alpha => x,
                        _ => -try!(self.run(-beta, -alpha, depth - 1)),
                    }
                };
                self.undo_move();

                // See how good this move was.
                if value >= beta {
                    // This move is so good, that the opponent will
                    // probably not allow this line of play to
                    // happen. Therefore we should not lose any more time
                    // on this position.
                    alpha = beta;
                    bound = BOUND_LOWER;
                    best_move = m;
                    break;
                }
                if value > alpha {
                    // We found a new best move.
                    alpha = value;
                    bound = BOUND_EXACT;
                    best_move = m;
                }
            }

            // Check if we are in a final position (no legal moves).
            if no_moves_yet {
                alpha = self.position.evaluate_final();
                bound = BOUND_EXACT;
            }
        }

        self.store(alpha, bound, depth, best_move);
        self.node_end();
        Ok(alpha)
    }

    /// Returns the number of searched positions.
    #[inline(always)]
    pub fn node_count(&self) -> NodeCount {
        self.reported_nodes + self.unreported_nodes
    }

    /// Resets the instance to the state it had when it was created.
    #[inline]
    pub fn reset(&mut self) {
        while self.moves.ply() > self.moves_starting_ply {
            self.moves.restore();
        }
        self.state_stack.clear();
        self.reported_nodes = 0;
        self.unreported_nodes = 0;
    }

    // Declares that we are starting to process a new node.
    //
    // Each recursive call to `run` begins with a call to
    // `node_begin`. The returned value is a TT entry telling
    // everything we know about the current position.
    #[inline]
    fn node_begin(&mut self) -> EntryData {
        // Consult the transposition table.
        let entry = if let Some(e) = self.tt.probe(self.position.hash()) {
            e
        } else {
            EntryData::new(0, BOUND_NONE, 0, 0, self.position.evaluate_static())
        };
        self.state_stack.push(NodeState {
            phase: NodePhase::Pristine,
            entry: entry,
        });
        entry
    }

    // Declares that we are done processing the current node.
    //
    // Each recursive call to `run` ends with a call to `node_end`.
    #[inline]
    fn node_end(&mut self) {
        if let NodePhase::Pristine = self.state_stack.last().unwrap().phase {
            // For pristine nodes we have not saved the move list
            // yet, so we should not restore it.
        } else {
            self.moves.restore();
        }
        self.state_stack.pop();
    }

    // Plays the next legal move in the current position and returns
    // it.
    //
    // Each call to `do_move` for the same position will play and
    // return a different move. When all legal moves has been played,
    // `None` will be returned. `do_move` will do whatever it can to
    // play the best moves first, and the worst last. It will also try
    // to be efficient, for example it will generate the list of all
    // pseudo-legal moves at the last possible moment.
    #[inline]
    fn do_move(&mut self) -> Option<Move> {
        let state = self.state_stack.last_mut().unwrap();

        if let NodePhase::Pristine = state.phase {
            // We save the move list at the last possible moment,
            // because most of the nodes are leafs.
            self.moves.save();

            // We always try the hash move first.
            state.phase = NodePhase::TriedHashMove;
            if state.entry.move16() != 0 {
                if let Some(m) = self.position.try_move_digest(state.entry.move16()) {
                    if self.position.do_move(m) {
                        return Some(m);
                    }
                }
            }
        }

        if let NodePhase::TriedHashMove = state.phase {
            // After the hash move, we generate all pseudo-legal
            // moves. But we should not forget to remove the already
            // tried hash move from the list.
            self.position.generate_moves(self.moves);
            if state.entry.move16() != 0 {
                self.moves.remove_move(state.entry.move16());
            }
            state.phase = NodePhase::GeneratedMoves;
        }

        // For the last, we spit the generated moves out.
        while let Some(m) = self.moves.remove_best_move() {
            if self.position.do_move(m) {
                return Some(m);
            }
        }
        None
    }

    // Takes the last played move back.
    #[inline]
    fn undo_move(&mut self) {
        self.position.undo_move();
    }

    // Stores updated node information in the transposition table.
    #[inline]
    fn store(&mut self, value: Value, bound: BoundType, depth: u8, best_move: Move) {
        let entry = &self.state_stack.last().unwrap().entry;
        let move16 = match best_move.digest() {
            0 => entry.move16(),
            x => x,
        };
        self.tt.store(self.position.hash(),
                      EntryData::new(value, bound, depth, move16, entry.eval_value()));
    }

    // Reports search progress.
    //
    // From time to time, we should report how many nodes had been
    // searched since the beginning of the search. This also gives an
    // opportunity for the search to be terminated.
    #[inline]
    fn report_progress(&mut self, new_nodes: NodeCount) -> Result<(), TerminatedSearch> {
        self.unreported_nodes += new_nodes;
        if self.unreported_nodes > NODE_COUNT_REPORT_INTERVAL {
            self.reported_nodes += self.unreported_nodes;
            self.unreported_nodes = 0;
            if (*self.report_function)(self.reported_nodes) {
                return Err(TerminatedSearch);
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::Search;
    use chess_move::*;
    use tt::*;
    use position::Position;

    #[test]
    fn test_search() {
        let p = Position::from_fen("8/8/8/8/3q3k/7n/6PP/2Q2R1K b - - 0 1").ok().unwrap();
        let tt = TranspositionTable::new();
        let mut moves = MoveStack::new();
        let mut report = |_| false;
        let mut search = Search::new(p, &tt, &mut moves, &mut report);
        let value = search.run(-30000, 30000, 2)
                          .ok()
                          .unwrap();
        assert!(value < -300);
        search.reset();
        let value = search.run(-30000, 30000, 4)
                          .ok()
                          .unwrap();
        assert!(value >= 20000);
    }
}
<|endoftext|>