<commit_before>// Licensed under the Apache License, Version 2.0
// <http://www.apache.org/licenses/LICENSE-2.0>

//! # Rust Random Choice
//! This is an implementation of the stochastic universal sampling algorithm:
//! https://en.wikipedia.org/wiki/Stochastic_universal_sampling
//!
//! ## Advantages
//! - Blazingly fast: O(n) (Roulette wheel selection algorithm: O(n * log n))
//! - Low Memory Usage: O(n); in place variant: O(1)
//! - There is a good diversity for the case, that all weights are equally distributed (in contrast to the roulette wheel selection algorithm which tends to select the same sample n times)
//! - The sum of the weights don't have to be 1.0, but must not overflow
//!
//! ## Applications
//! - **Evolutionary algorithms**: Choose the _n_ fittest populations by their fitness **_fi_**
//! - **Monte Carlo Localization**: Resampling of _n_ particles by their weight **_w_**
//!
//! # Examples
//! ## In Place Variant
//! ```
//! extern crate random_choice;
//! use self::random_choice::random_choice;
//!
//! # fn main() {
//! let mut samples = vec!["hi", "this", "is", "a", "test!"];
//! let weights: Vec<f64> = vec![5.6, 7.8, 9.7, 1.1, 2.0];
//!
//! random_choice().random_choice_in_place_f64(&mut samples, &weights);
//!
//! for sample in samples {
//!     print!("{}, ", sample);
//! }
//! # }
//! ```
//! ## N Selection Variant
//! ```
//! extern crate random_choice;
//! use self::random_choice::random_choice;
//!
//! # fn main() {
//! let capacity: usize = 500;
//! let mut samples: Vec<usize> = Vec::with_capacity(capacity);
//! let mut weights: Vec<f64> = Vec::with_capacity(capacity);
//!
//! for i in 0..capacity {
//!     samples.push(i);
//!     weights.push(i as f64);
//! }
//!
//! let number_choices = 10000;
//! let choices = random_choice().random_choice_f64(&samples, &weights, number_choices);
//!
//! assert!(choices.len() == number_choices);
//!
//! for choice in choices {
//!     print!("{}, ", choice);
//! }
//! # }
//! ```
//! ## With custom seed
//! ```
//! extern crate random_choice;
//! extern crate rand;
//!
//! use self::random_choice::RandomChoice;
//! use self::rand::thread_rng;
//!
//! # fn main() {
//! let mut samples = vec!["hi", "this", "is", "a", "test!"];
//! let weights: Vec<f64> = vec![5.6, 7.8, 9.7, 1.1, 2.0];
//!
//! let mut random_choice = RandomChoice::new(thread_rng());
//! random_choice.random_choice_in_place_f64(&mut samples, &weights);
//!
//! for sample in samples {
//!     print!("{}, ", sample);
//! }
//! # }
//! ```

extern crate rand;

use self::rand::{thread_rng, ThreadRng, Rng};

pub struct RandomChoice<RNG: Rng> {
    rng: RNG,
}

/// Creates a new RandomChoice struct using the ThreadRng
pub fn random_choice() -> RandomChoice<ThreadRng> {
    RandomChoice::new(thread_rng())
}

impl<RNG: Rng> RandomChoice<RNG> {
    /// Creates a new RandomChoice struct.
    /// @param rng the random number generator to use with this stuct.
    pub fn new(rng: RNG) -> Self {
        RandomChoice { rng: rng }
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    ///
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    /// @param n Number of randomly chosen samples by weight.
    /// @return randomly selected samples by their weights
    pub fn random_choice_f64<'a, T>(&mut self,
                                    samples: &'a [T],
                                    weights: &[f64],
                                    n: usize)
                                    -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 {
            return Vec::new();
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i];
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while choices.len() < weights.len() {
            choices.push(&samples[i]);
        }

        choices
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    /// The result gets saved directly in the samples argument.
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    pub fn random_choice_in_place_f64<T: Clone>(&mut self, samples: &mut [T], weights: &[f64]) {
        if weights.len() < 2 {
            return;
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut j: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j];
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
            i += 1
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while i < weights.len() {
            samples[i] = samples[j].clone();
            i += 1
        }

    }

    pub fn random_choice_f32<'a, T>(&mut self,
                                    samples: &'a [T],
                                    weights: &[f32],
                                    n: usize)
                                    -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 {
            return Vec::new();
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i as f64);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0] as f64;
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i] as f64;
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while choices.len() < weights.len() {
            choices.push(&samples[i]);
        }

        choices
    }

    pub fn random_choice_in_place_f32<T: Clone>(&mut self, samples: &mut [T], weights: &[f32]) {
        if weights.len() < 2 {
            return;
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i as f64);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut j: usize = 0;
        let mut accumulated_weights = weights[0] as f64;
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j] as f64;
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
            i += 1
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while i < weights.len() {
            samples[i] = samples[j].clone();
            i += 1
        }
    }
}<commit_msg>comment modified<commit_after>// Licensed under the Apache License, Version 2.0
// <http://www.apache.org/licenses/LICENSE-2.0>

//! # Rust Random Choice
//! This is an implementation of the stochastic universal sampling algorithm:
//! https://en.wikipedia.org/wiki/Stochastic_universal_sampling
//!
//! ## Advantages
//! - Blazingly fast: O(n) (Roulette wheel selection algorithm: O(n * log n))
//! - Low Memory Usage: O(n); in place variant: O(1)
//! - There is a good diversity for the case, that all weights are equally distributed (in contrast to the roulette wheel selection algorithm which tends to select the same sample n times)
//! - The sum of the weights don't have to be 1.0, but must not overflow
//!
//! ## Applications
//! - **Evolutionary algorithms**: Choose the _n_ fittest populations by their fitness **_fi_**
//! - **Monte Carlo Localization**: Resampling of _n_ particles by their weight **_w_**
//!
//! # Examples
//! ## In Place Variant
//! ```
//! extern crate random_choice;
//! use self::random_choice::random_choice;
//!
//! # fn main() {
//! let mut samples = vec!["hi", "this", "is", "a", "test!"];
//! let weights: Vec<f64> = vec![5.6, 7.8, 9.7, 1.1, 2.0];
//!
//! random_choice().random_choice_in_place_f64(&mut samples, &weights);
//!
//! for sample in samples {
//!     print!("{}, ", sample);
//! }
//! # }
//! ```
//! ## N Selection Variant
//! ```
//! extern crate random_choice;
//! use self::random_choice::random_choice;
//!
//! # fn main() {
//! let capacity: usize = 500;
//! let mut samples: Vec<usize> = Vec::with_capacity(capacity);
//! let mut weights: Vec<f64> = Vec::with_capacity(capacity);
//!
//! for i in 0..capacity {
//!     samples.push(i);
//!     weights.push(i as f64);
//! }
//!
//! let number_choices = 10000;
//! let choices = random_choice().random_choice_f64(&samples, &weights, number_choices);
//!
//! assert!(choices.len() == number_choices);
//!
//! for choice in choices {
//!     print!("{}, ", choice);
//! }
//! # }
//! ```
//! ## With custom seed
//! ```
//! extern crate random_choice;
//! extern crate rand;
//!
//! use self::random_choice::RandomChoice;
//! use self::rand::thread_rng;
//!
//! # fn main() {
//! let mut samples = vec!["hi", "this", "is", "a", "test!"];
//! let weights: Vec<f64> = vec![5.6, 7.8, 9.7, 1.1, 2.0];
//!
//! let mut random_choice = RandomChoice::new(thread_rng());
//! random_choice.random_choice_in_place_f64(&mut samples, &weights);
//!
//! for sample in samples {
//!     print!("{}, ", sample);
//! }
//! # }
//! ```

extern crate rand;

use self::rand::{thread_rng, ThreadRng, Rng};

pub struct RandomChoice<RNG: Rng> {
    rng: RNG,
}

/// Creates a new RandomChoice struct using the ThreadRng
pub fn random_choice() -> RandomChoice<ThreadRng> {
    RandomChoice::new(thread_rng())
}

impl<RNG: Rng> RandomChoice<RNG> {
    /// Creates a new RandomChoice struct.
    /// @param rng the random number generator to use with this stuct.
    pub fn new(rng: RNG) -> Self {
        RandomChoice { rng: rng }
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    ///
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    /// @param n Number of randomly chosen samples by weight.
    /// @return randomly selected samples by their weights
    pub fn random_choice_f64<'a, T>(&mut self,
                                    samples: &'a [T],
                                    weights: &[f64],
                                    n: usize)
                                    -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 {
            return Vec::new();
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i];
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        // add this condition, because float leads to inaccurate
        // calculations which can miss some samples
        while choices.len() < weights.len() {
            choices.push(&samples[i]);
        }

        choices
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    /// The result gets saved directly in the samples argument.
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    pub fn random_choice_in_place_f64<T: Clone>(&mut self, samples: &mut [T], weights: &[f64]) {
        if weights.len() < 2 {
            return;
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut j: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j];
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
            i += 1
        }

        // add this condition, because float leads to inaccurate
        // calculations which can miss some samples
        while i < weights.len() {
            samples[i] = samples[j].clone();
            i += 1
        }

    }

    pub fn random_choice_f32<'a, T>(&mut self,
                                    samples: &'a [T],
                                    weights: &[f32],
                                    n: usize)
                                    -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 {
            return Vec::new();
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i as f64);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0] as f64;
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i] as f64;
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        // add this condition, because float leads to inaccurate
        // calculations which can miss some samples
        while choices.len() < weights.len() {
            choices.push(&samples[i]);
        }

        choices
    }

    pub fn random_choice_in_place_f32<T: Clone>(&mut self, samples: &mut [T], weights: &[f32]) {
        if weights.len() < 2 {
            return;
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i as f64);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut j: usize = 0;
        let mut accumulated_weights = weights[0] as f64;
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j] as f64;
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
            i += 1
        }

        // add this condition, because float leads to inaccurate
        // calculations which can miss some samples
        while i < weights.len() {
            samples[i] = samples[j].clone();
            i += 1
        }
    }
}<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>

// TODO: collapse. Well, do tables first.
${helpers.single_keyword("visibility",
                         "visible hidden",
                         extra_gecko_values="collapse",
                         gecko_ffi_name="mVisible",
                         animation_type="normal",
                         spec="https://drafts.csswg.org/css-box/#propdef-visibility")}

// CSS Writing Modes Level 3
// https://drafts.csswg.org/css-writing-modes-3
${helpers.single_keyword("writing-mode",
                         "horizontal-tb vertical-rl vertical-lr",
                         extra_gecko_values="sideways-rl sideways-lr",
                         experimental=True,
                         need_clone=True,
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode")}

${helpers.single_keyword("direction", "ltr rtl", need_clone=True, animation_type="none",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction",
                         needs_conversion=True)}

<%helpers:single_keyword_computed
    name="text-orientation"
    values="mixed upright sideways"
    extra_specified="sideways-right"
    products="gecko"
    need_clone="True"
    animation_type="none"
    spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation"
>
    use values::HasViewportPercentage;
    no_viewport_percentage!(SpecifiedValue);

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _: &Context) -> computed_value::T {
            match *self {
                % for value in "mixed upright sideways".split():
                    SpecifiedValue::${value} => computed_value::T::${value},
                % endfor
                // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-sideways-right
                SpecifiedValue::sideways_right => computed_value::T::sideways,
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue {
            match *computed {
                % for value in "mixed upright sideways".split():
                    computed_value::T::${value} => SpecifiedValue::${value},
                % endfor
            }
        }
    }
</%helpers:single_keyword_computed>

// CSS Color Module Level 4
// https://drafts.csswg.org/css-color/
${helpers.single_keyword("color-adjust",
                         "economy exact", products="gecko",
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-color/#propdef-color-adjust")}

<% image_rendering_custom_consts = { "crisp-edges": "CRISPEDGES",
                                     "-moz-crisp-edges": "CRISPEDGES" } %>
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
${helpers.single_keyword("image-rendering",
                         "auto",
                         extra_gecko_values="optimizespeed optimizequality -moz-crisp-edges",
                         extra_servo_values="pixelated crisp-edges",
                         custom_consts=image_rendering_custom_consts,
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-images/#propdef-image-rendering")}

// Image Orientation
<%helpers:longhand name="image-orientation"
                   products="gecko"
                   animation_type="none"
    spec="https://drafts.csswg.org/css-images/#propdef-image-orientation, \
      /// additional values in https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation">
    use std::fmt;
    use style_traits::ToCss;
    use values::specified::Angle;

    use values::HasViewportPercentage;
    no_viewport_percentage!(SpecifiedValue);

    use std::f32::consts::PI;
    use values::CSSFloat;
    const TWO_PI: CSSFloat = 2.0 * PI;

    #[derive(Clone, PartialEq, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub angle: Option<Angle>,
        pub flipped: bool
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if let Some(angle) = self.angle {
                try!(angle.to_css(dest));
                if self.flipped {
                    dest.write_str(" flipped")
                } else {
                    Ok(())
                }
            } else {
                if self.flipped {
                    dest.write_str("flipped")
                } else {
                    dest.write_str("from-image")
                }
            }
        }
    }

    pub mod computed_value {
        use values::computed::Angle;

        #[derive(Clone, PartialEq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum T {
            FromImage,
            AngleWithFlipped(Angle, bool),
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T::AngleWithFlipped(computed::Angle::zero(), false)
    }

    // According to CSS Content Module Level 3:
    // The computed value of the property is calculated by rounding the specified angle
    // to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn.
    #[inline]
    fn normalize_angle(angle: &computed::Angle) -> computed::Angle {
        let radians = angle.radians();
        let rounded_quarter_turns = (4.0 * radians / TWO_PI).round();
        let normalized_quarter_turns = (rounded_quarter_turns % 4.0 + 4.0) % 4.0;
        let normalized_radians = normalized_quarter_turns/4.0 * TWO_PI;
        computed::Angle::from_radians(normalized_radians)
    }

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, context: &Context) -> computed_value::T {
            if let Some(ref angle) = self.angle {
                let angle = angle.to_computed_value(context);
                let normalized_angle = normalize_angle(&angle);
                computed_value::T::AngleWithFlipped(normalized_angle, self.flipped)
            } else {
                if self.flipped {
                    computed_value::T::AngleWithFlipped(computed::Angle::zero(), true)
                } else {
                    computed_value::T::FromImage
                }
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> Self {
            match *computed {
                computed_value::T::FromImage => SpecifiedValue { angle: None, flipped: false },
                computed_value::T::AngleWithFlipped(ref angle, flipped) => {
                    SpecifiedValue {
                        angle: Some(Angle::from_computed_value(angle)),
                        flipped: flipped,
                    }
                }
            }
        }
    }

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self {
                computed_value::T::FromImage => dest.write_str("from-image"),
                computed_value::T::AngleWithFlipped(angle, flipped) => {
                    try!(angle.to_css(dest));
                    if flipped {
                        try!(dest.write_str(" flipped"));
                    }
                    Ok(())
                },
            }
        }
    }

    // from-image | <angle> | [<angle>? flip]
    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        if input.try(|input| input.expect_ident_matching("from-image")).is_ok() {
            // Handle from-image
            Ok(SpecifiedValue { angle: None, flipped: false })
        } else if input.try(|input| input.expect_ident_matching("flip")).is_ok() {
            // Handle flip
            Ok(SpecifiedValue { angle: Some(Angle::zero()), flipped: true })
        } else {
            // Handle <angle> | <angle> flip
            let angle = input.try(|input| Angle::parse(context, input)).ok();
            if angle.is_none() {
                return Err(());
            }

            let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok();
            Ok(SpecifiedValue { angle: angle, flipped: flipped })
        }
    }
</%helpers:longhand>

// Used in the bottom-up flow construction traversal to avoid constructing flows for
// descendants of nodes with `display: none`.
<%helpers:longhand name="-servo-under-display-none"
                   derived_from="display"
                   products="servo"
                   animation_type="none"
                   spec="Nonstandard (internal layout use only)">
    use std::fmt;
    use style_traits::ToCss;
    use values::computed::ComputedValueAsSpecified;
    use values::HasViewportPercentage;

    no_viewport_percentage!(SpecifiedValue);

    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
    pub struct SpecifiedValue(pub bool);

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }
    impl ComputedValueAsSpecified for SpecifiedValue {}

    pub fn get_initial_value() -> computed_value::T {
        SpecifiedValue(false)
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
            Ok(()) // Internal property
        }
    }

    #[inline]
    pub fn derive_from_display(context: &mut Context) {
        use super::display::computed_value::T as Display;

        if context.style().get_box().clone_display() == Display::none {
            context.mutate_style().mutate_inheritedbox()
                                  .set__servo_under_display_none(SpecifiedValue(true));
        }
    }
</%helpers:longhand>
<commit_msg>style: Add Gecko-only support for the deprecated SVG 1.1 writing-mode aliases.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>

// TODO: collapse. Well, do tables first.
${helpers.single_keyword("visibility",
                         "visible hidden",
                         extra_gecko_values="collapse",
                         gecko_ffi_name="mVisible",
                         animation_type="normal",
                         spec="https://drafts.csswg.org/css-box/#propdef-visibility")}

// CSS Writing Modes Level 3
// https://drafts.csswg.org/css-writing-modes-3
${helpers.single_keyword("writing-mode",
                         "horizontal-tb vertical-rl vertical-lr",
                         extra_gecko_values="sideways-rl sideways-lr",
                         extra_gecko_aliases="lr=horizontal-tb lr-tb=horizontal-tb \
                                              rl=horizontal-tb rl-tb=horizontal-tb \
                                              tb=vertical-rl   tb-rl=vertical-rl",
                         experimental=True,
                         need_clone=True,
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode")}

${helpers.single_keyword("direction", "ltr rtl", need_clone=True, animation_type="none",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction",
                         needs_conversion=True)}

<%helpers:single_keyword_computed
    name="text-orientation"
    values="mixed upright sideways"
    extra_specified="sideways-right"
    products="gecko"
    need_clone="True"
    animation_type="none"
    spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation"
>
    use values::HasViewportPercentage;
    no_viewport_percentage!(SpecifiedValue);

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _: &Context) -> computed_value::T {
            match *self {
                % for value in "mixed upright sideways".split():
                    SpecifiedValue::${value} => computed_value::T::${value},
                % endfor
                // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-sideways-right
                SpecifiedValue::sideways_right => computed_value::T::sideways,
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue {
            match *computed {
                % for value in "mixed upright sideways".split():
                    computed_value::T::${value} => SpecifiedValue::${value},
                % endfor
            }
        }
    }
</%helpers:single_keyword_computed>

// CSS Color Module Level 4
// https://drafts.csswg.org/css-color/
${helpers.single_keyword("color-adjust",
                         "economy exact", products="gecko",
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-color/#propdef-color-adjust")}

<% image_rendering_custom_consts = { "crisp-edges": "CRISPEDGES",
                                     "-moz-crisp-edges": "CRISPEDGES" } %>
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
${helpers.single_keyword("image-rendering",
                         "auto",
                         extra_gecko_values="optimizespeed optimizequality -moz-crisp-edges",
                         extra_servo_values="pixelated crisp-edges",
                         custom_consts=image_rendering_custom_consts,
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-images/#propdef-image-rendering")}

// Image Orientation
<%helpers:longhand name="image-orientation"
                   products="gecko"
                   animation_type="none"
    spec="https://drafts.csswg.org/css-images/#propdef-image-orientation, \
      /// additional values in https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation">
    use std::fmt;
    use style_traits::ToCss;
    use values::specified::Angle;

    use values::HasViewportPercentage;
    no_viewport_percentage!(SpecifiedValue);

    use std::f32::consts::PI;
    use values::CSSFloat;
    const TWO_PI: CSSFloat = 2.0 * PI;

    #[derive(Clone, PartialEq, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub angle: Option<Angle>,
        pub flipped: bool
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if let Some(angle) = self.angle {
                try!(angle.to_css(dest));
                if self.flipped {
                    dest.write_str(" flipped")
                } else {
                    Ok(())
                }
            } else {
                if self.flipped {
                    dest.write_str("flipped")
                } else {
                    dest.write_str("from-image")
                }
            }
        }
    }

    pub mod computed_value {
        use values::computed::Angle;

        #[derive(Clone, PartialEq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum T {
            FromImage,
            AngleWithFlipped(Angle, bool),
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T::AngleWithFlipped(computed::Angle::zero(), false)
    }

    // According to CSS Content Module Level 3:
    // The computed value of the property is calculated by rounding the specified angle
    // to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn.
    #[inline]
    fn normalize_angle(angle: &computed::Angle) -> computed::Angle {
        let radians = angle.radians();
        let rounded_quarter_turns = (4.0 * radians / TWO_PI).round();
        let normalized_quarter_turns = (rounded_quarter_turns % 4.0 + 4.0) % 4.0;
        let normalized_radians = normalized_quarter_turns/4.0 * TWO_PI;
        computed::Angle::from_radians(normalized_radians)
    }

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, context: &Context) -> computed_value::T {
            if let Some(ref angle) = self.angle {
                let angle = angle.to_computed_value(context);
                let normalized_angle = normalize_angle(&angle);
                computed_value::T::AngleWithFlipped(normalized_angle, self.flipped)
            } else {
                if self.flipped {
                    computed_value::T::AngleWithFlipped(computed::Angle::zero(), true)
                } else {
                    computed_value::T::FromImage
                }
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> Self {
            match *computed {
                computed_value::T::FromImage => SpecifiedValue { angle: None, flipped: false },
                computed_value::T::AngleWithFlipped(ref angle, flipped) => {
                    SpecifiedValue {
                        angle: Some(Angle::from_computed_value(angle)),
                        flipped: flipped,
                    }
                }
            }
        }
    }

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self {
                computed_value::T::FromImage => dest.write_str("from-image"),
                computed_value::T::AngleWithFlipped(angle, flipped) => {
                    try!(angle.to_css(dest));
                    if flipped {
                        try!(dest.write_str(" flipped"));
                    }
                    Ok(())
                },
            }
        }
    }

    // from-image | <angle> | [<angle>? flip]
    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        if input.try(|input| input.expect_ident_matching("from-image")).is_ok() {
            // Handle from-image
            Ok(SpecifiedValue { angle: None, flipped: false })
        } else if input.try(|input| input.expect_ident_matching("flip")).is_ok() {
            // Handle flip
            Ok(SpecifiedValue { angle: Some(Angle::zero()), flipped: true })
        } else {
            // Handle <angle> | <angle> flip
            let angle = input.try(|input| Angle::parse(context, input)).ok();
            if angle.is_none() {
                return Err(());
            }

            let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok();
            Ok(SpecifiedValue { angle: angle, flipped: flipped })
        }
    }
</%helpers:longhand>

// Used in the bottom-up flow construction traversal to avoid constructing flows for
// descendants of nodes with `display: none`.
<%helpers:longhand name="-servo-under-display-none"
                   derived_from="display"
                   products="servo"
                   animation_type="none"
                   spec="Nonstandard (internal layout use only)">
    use std::fmt;
    use style_traits::ToCss;
    use values::computed::ComputedValueAsSpecified;
    use values::HasViewportPercentage;

    no_viewport_percentage!(SpecifiedValue);

    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
    pub struct SpecifiedValue(pub bool);

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }
    impl ComputedValueAsSpecified for SpecifiedValue {}

    pub fn get_initial_value() -> computed_value::T {
        SpecifiedValue(false)
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
            Ok(()) // Internal property
        }
    }

    #[inline]
    pub fn derive_from_display(context: &mut Context) {
        use super::display::computed_value::T as Display;

        if context.style().get_box().clone_display() == Display::none {
            context.mutate_style().mutate_inheritedbox()
                                  .set__servo_under_display_none(SpecifiedValue(true));
        }
    }
</%helpers:longhand>
<|endoftext|>


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

#![deny(missing_docs)]

//! # crumsort
//!
//! A parallelized Rust port of [crumsort](https://github.com/scandum/crumsort).
//!
//! # Usage
//!
//! ```
//! use crumsort::ParCrumSort;
//!
//! let mut vals = [5, 4, 1, 3, 2];
//!
//! vals.par_crumsort();
//!
//! assert_eq!(vals, [1, 2, 3, 4, 5]);
//! ```

mod quadsort;

const SWAP_SIZE: usize = 512;
const CRUM_CHUNK_SIZE: usize = 32;
const CRUM_OUT: usize = 24;
const JOIN_THRESHOLD: usize = 1_024;

#[allow(clippy::assertions_on_constants)]
const _: () = assert!(2 * CRUM_CHUNK_SIZE <= SWAP_SIZE);

pub(crate) trait Sortable: Copy + Default + Ord {}

impl<T: Copy + Default + Ord> Sortable for T {}

fn crum_median_of_sqrt<T: Sortable>(slice: &mut [T], swap: &mut [T]) -> usize {
    let sqrt = match slice.len() {
        0..=65_535 => 16,
        65_536..=262_143 => 128,
        _ => 256,
    };

    let div = slice.len() / sqrt;
    let mut end = slice.len() - 1;

    for i in (0..sqrt).rev() {
        slice.swap(i, end);

        end = end.saturating_sub(div);
    }

    quadsort::quadsort_swap(&mut slice[..sqrt], swap);

    sqrt / 2
}

fn crum_median_of_three<T: Sortable>(slice: &[T], indices: [usize; 3]) -> usize {
    let x = u8::from(slice[indices[0]] > slice[indices[1]]);
    let y = u8::from(slice[indices[0]] > slice[indices[2]]);
    let z = u8::from(slice[indices[1]] > slice[indices[2]]);

    indices[usize::from(u8::from(x == y) + (y ^ z))]
}

fn crum_median_of_nine<T: Sortable>(slice: &[T]) -> usize {
    let div = slice.len() / 16;

    let x = crum_median_of_three(slice, [div * 2, div, div * 4]);
    let y = crum_median_of_three(slice, [div * 8, div * 6, div * 10]);
    let z = crum_median_of_three(slice, [div * 14, div * 12, div * 15]);

    crum_median_of_three(slice, [x, y, z])
}

#[derive(Debug)]
struct Partitioner {
    left_i: usize,
    right_i: usize,
    end_i: usize,
    cursor: usize,
}

impl Partitioner {
    pub const fn new<const C: usize>(len: usize) -> Self {
        Self {
            left_i: C,
            right_i: len - 1 - C,
            end_i: len - 1,
            cursor: 0,
        }
    }

    pub fn next<T: Sortable, const IS_LEFT: bool>(
        &mut self,
        slice: &mut [T],
        swap: Option<&mut [T]>,
        pivot: T,
    ) {
        let val = {
            let i = if IS_LEFT { self.left_i } else { self.right_i };
            let val = swap.map_or_else(
                || {
                    // SAFETY:
                    // `Partitioner::next` is called `slice.len()` times ==> 0 <= i < `slice.len()`
                    unsafe { *slice.get_unchecked(i) }
                },
                |swap| {
                    // SAFETY:
                    // `Partitioner::next` is called `slice.len()` times ==> 0 <= i < `slice.len()` (1)
                    // i < 2 * CRUM_CHUNK_SIZE (2)
                    // 2 * CRUM_CHUNK_SIZE <= `swap.len()` (3)
                    // (1), (2), (3) ==> 0 <= i < `swap.len()`
                    unsafe { *swap.get_unchecked(i) }
                },
            );

            // SAFETY:
            // `usize::from(val <= pivot)` <= 1, `Partitioner::next` is called `slice.len()`
            // times ==> self.cursor <= i` (1)
            // (1), i < `slice.len()` ==> self.cursor < `slice.len()`
            unsafe {
                *slice.get_unchecked_mut(self.cursor) = val;
            }

            // SAFETY:
            // for nth iteration:
            //   `usize::from(val <= pivot)` <= 1 ==> self.cursor <= n (1)
            //   self.end_i = `slice.len()` - 1 - n (2)
            //   (1), (2) ==> self.cursor + self.end_i <= `slice.len()` - 1 <==>
            //   <==> self.cursor + self.end_i < `slice.len()`
            unsafe {
                *slice.get_unchecked_mut(self.cursor + self.end_i) = val;
            }

            val
        };

        if IS_LEFT {
            self.left_i += 1;
        } else {
            self.right_i -= 1;
        }

        self.end_i = self.end_i.overflowing_sub(1).0;
        self.cursor += usize::from(val <= pivot);
    }
}

fn fulcrum_partition_inner<T: Sortable>(slice: &mut [T], swap: &mut [T], pivot: T) -> usize {
    if slice.len() <= swap.len() {
        const CHUNK: usize = 8;

        let mut i = 0;
        let mut cursor = 0;

        let mut partition = |slice: &mut [T]| {
            let val = {
                // SAFETY:
                // `partition` is called `slice.len()` times ==> i < `slice.len()`
                let val = unsafe { *slice.get_unchecked(i) };

                // SAFETY:
                // `swap.len` >= `slice.len()` ==> i < `swap.len()` (1)
                // `usize::from(val <= pivot)` <= 1, `partition` is called `slice.len()` times ==>
                // => cursor <= i (2)
                // (1), (2) ==> 0 <= i - cursor < `swap.len()`
                unsafe {
                    *swap.get_unchecked_mut(i - cursor) = val;
                }

                // SAFETY:
                // cursor <= i, i < `slice.len()` ==> cursor < `slice.len()`
                unsafe {
                    *slice.get_unchecked_mut(cursor) = val;
                }

                val
            };

            i += 1;
            cursor += usize::from(val <= pivot);
        };

        for _ in 0..slice.len() / CHUNK {
            for _ in 0..CHUNK {
                partition(slice);
            }
        }

        for _ in 0..slice.len() % CHUNK {
            partition(slice);
        }

        let len = slice.len();
        slice[cursor..].copy_from_slice(&swap[..len - cursor]);

        return cursor;
    }

    swap[..CRUM_CHUNK_SIZE].copy_from_slice(&slice[..CRUM_CHUNK_SIZE]);
    swap[CRUM_CHUNK_SIZE..2 * CRUM_CHUNK_SIZE]
        .copy_from_slice(&slice[slice.len() - CRUM_CHUNK_SIZE..]);

    let mut partitioner = Partitioner::new::<CRUM_CHUNK_SIZE>(slice.len());

    let mut count = slice.len() / CRUM_CHUNK_SIZE - 2;

    loop {
        if partitioner.left_i - partitioner.cursor <= CRUM_CHUNK_SIZE {
            if let Some(new_count) = count.checked_sub(1) {
                count = new_count;
            } else {
                break;
            }

            for _ in 0..CRUM_CHUNK_SIZE {
                partitioner.next::<_, true>(slice, None, pivot);
            }
        }

        if partitioner.left_i - partitioner.cursor > CRUM_CHUNK_SIZE {
            if let Some(new_count) = count.checked_sub(1) {
                count = new_count;
            } else {
                break;
            }

            for _ in 0..CRUM_CHUNK_SIZE {
                partitioner.next::<_, false>(slice, None, pivot);
            }
        }
    }

    if partitioner.left_i - partitioner.cursor <= CRUM_CHUNK_SIZE {
        for _ in 0..slice.len() % CRUM_CHUNK_SIZE {
            partitioner.next::<_, true>(slice, None, pivot);
        }
    } else {
        for _ in 0..slice.len() % CRUM_CHUNK_SIZE {
            partitioner.next::<_, false>(slice, None, pivot);
        }
    }

    partitioner.left_i = 0;
    for _ in 0..2 * CRUM_CHUNK_SIZE {
        partitioner.next::<_, true>(slice, Some(swap), pivot);
    }

    partitioner.cursor
}

fn fulcrum_partition<T: Sortable + Send>(slice: &mut [T], swap: &mut [T], max: Option<T>) {
    let i = if slice.len() <= 2_048 {
        crum_median_of_nine(slice)
    } else {
        crum_median_of_sqrt(slice, swap)
    };

    let pivot = slice[i];

    if let Some(max_val) = max {
        if max_val <= pivot {
            let left = fulcrum_partition_inner(slice, swap, pivot);
            let right = slice.len() - left;

            if right <= left / 16 || left <= CRUM_OUT {
                return quadsort::quadsort_swap(&mut slice[..left], swap);
            }

            return fulcrum_partition(&mut slice[..left], swap, None);
        }
    }

    let len = slice.len() - 1;

    slice[i] = slice[len];

    let left_i = fulcrum_partition_inner(&mut slice[..len], swap, pivot);
    let right_i = len - left_i;

    slice[len] = slice[left_i];
    slice[left_i] = pivot;

    if left_i <= right_i / 16 || right_i <= CRUM_OUT {
        if right_i == 0 {
            let left = fulcrum_partition_inner(&mut slice[..left_i], swap, pivot);
            let right = len - left;

            if right <= left / 16 || left <= CRUM_OUT {
                return quadsort::quadsort_swap(&mut slice[..left], swap);
            }

            return fulcrum_partition(&mut slice[..left], swap, None);
        }

        quadsort::quadsort_swap(&mut slice[left_i + 1..], swap);
    }

    if slice.len() > JOIN_THRESHOLD {
        let (left, right) = slice.split_at_mut(left_i + 1);

        rayon::join(
            move || {
                if !(left_i <= right_i / 16 || right_i <= CRUM_OUT) {
                    fulcrum_partition(right, swap, max);
                }
            },
            || {
                let mut swap = [T::default(); SWAP_SIZE];

                if right_i <= left_i / 32 || left_i <= CRUM_OUT {
                    return quadsort::quadsort_swap(left, &mut swap);
                }

                let max = Some(left[left.len() - 1]);
                fulcrum_partition(left, &mut swap, max);
            },
        );
    } else {
        if !(left_i <= right_i / 16 || right_i <= CRUM_OUT) {
            fulcrum_partition(&mut slice[left_i + 1..], swap, max);
        }

        if right_i <= left_i / 32 || left_i <= CRUM_OUT {
            return quadsort::quadsort_swap(&mut slice[..left_i], swap);
        }

        let max = Some(slice[left_i]);
        fulcrum_partition(&mut slice[..left_i], swap, max);
    }
}

/// Parallel sort extension trait.
pub trait ParCrumSort {
    /// Unstably sorts the slice.
    ///
    /// # Examples
    ///
    /// ```
    /// use crumsort::ParCrumSort;
    ///
    /// let mut vals = [5, 4, 1, 3, 2];
    ///
    /// vals.par_crumsort();
    ///
    /// assert_eq!(vals, [1, 2, 3, 4, 5]);
    /// ```
    fn par_crumsort(&mut self);
}

impl<T: Copy + Default + Ord + Send> ParCrumSort for [T] {
    fn par_crumsort(&mut self) {
        if self.len() < 32 {
            return quadsort::tail_swap(self);
        }

        fulcrum_partition(self, &mut [T::default(); SWAP_SIZE], None);
    }
}

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

    use rand::prelude::*;

    fn test_partition<const N: usize>(thirds: usize) {
        let mut slice: Vec<_> = (0..N).into_iter().map(|i| N - i - 1).collect();

        let pivot = slice[thirds * slice.len() / 3];
        fulcrum_partition_inner(&mut slice, &mut [0; 2 * CRUM_CHUNK_SIZE], pivot);

        let (left, right) = slice.split_at(pivot + 1);

        assert!(left.iter().all(|&val| val <= pivot));
        assert!(right.iter().all(|&val| val > pivot));
    }

    #[test]
    fn partition7() {
        test_partition::<7>(1);
    }

    #[test]
    fn partition15() {
        test_partition::<15>(1);
    }

    #[test]
    fn partition31() {
        test_partition::<31>(1);
    }

    #[test]
    fn partition159() {
        test_partition::<159>(1);
    }

    #[test]
    fn partition193() {
        test_partition::<193>(2);
    }

    #[test]
    fn sort_16_384_uniform() {
        let mut rng = StdRng::seed_from_u64(42);
        let mut vals: Vec<u32> = (0..16_384).into_iter().map(|_| rng.gen()).collect();
        let mut sorted = vals.clone();

        sorted.sort_unstable();

        vals.as_mut_slice().par_crumsort();

        assert_eq!(vals, sorted);
    }

    #[test]
    fn sort_16_384_uniform_small_range() {
        let mut rng = StdRng::seed_from_u64(42);
        let mut vals: Vec<u32> = (0..16_384)
            .into_iter()
            .map(|_| rng.gen_range(0..64))
            .collect();
        let mut sorted = vals.clone();

        sorted.sort_unstable();

        vals.as_mut_slice().par_crumsort();

        assert_eq!(vals, sorted);
    }
}
<commit_msg>Added docs to constants.<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![deny(missing_docs)]

//! # crumsort
//!
//! A parallelized Rust port of [crumsort](https://github.com/scandum/crumsort).
//!
//! # Usage
//!
//! ```
//! use crumsort::ParCrumSort;
//!
//! let mut vals = [5, 4, 1, 3, 2];
//!
//! vals.par_crumsort();
//!
//! assert_eq!(vals, [1, 2, 3, 4, 5]);
//! ```

mod quadsort;

/// Size of tack-allocated swap memory for certain operations that can be performed faster with
/// swap memory than in-place.
const SWAP_SIZE: usize = 512;
/// Size of likely unrolled loops when performing partitioning that does not fit in swap.
const CRUM_CHUNK_SIZE: usize = 32;
/// Partition ratio at which point quadsort is used.
const CRUM_OUT: usize = 24;
/// Minimum slice length to consider running partitioning in parallel.
const JOIN_THRESHOLD: usize = 1_024;

#[allow(clippy::assertions_on_constants)]
const _: () = assert!(2 * CRUM_CHUNK_SIZE <= SWAP_SIZE);

pub(crate) trait Sortable: Copy + Default + Ord {}

impl<T: Copy + Default + Ord> Sortable for T {}

fn crum_median_of_sqrt<T: Sortable>(slice: &mut [T], swap: &mut [T]) -> usize {
    let sqrt = match slice.len() {
        0..=65_535 => 16,
        65_536..=262_143 => 128,
        _ => 256,
    };

    let div = slice.len() / sqrt;
    let mut end = slice.len() - 1;

    for i in (0..sqrt).rev() {
        slice.swap(i, end);

        end = end.saturating_sub(div);
    }

    quadsort::quadsort_swap(&mut slice[..sqrt], swap);

    sqrt / 2
}

fn crum_median_of_three<T: Sortable>(slice: &[T], indices: [usize; 3]) -> usize {
    let x = u8::from(slice[indices[0]] > slice[indices[1]]);
    let y = u8::from(slice[indices[0]] > slice[indices[2]]);
    let z = u8::from(slice[indices[1]] > slice[indices[2]]);

    indices[usize::from(u8::from(x == y) + (y ^ z))]
}

fn crum_median_of_nine<T: Sortable>(slice: &[T]) -> usize {
    let div = slice.len() / 16;

    let x = crum_median_of_three(slice, [div * 2, div, div * 4]);
    let y = crum_median_of_three(slice, [div * 8, div * 6, div * 10]);
    let z = crum_median_of_three(slice, [div * 14, div * 12, div * 15]);

    crum_median_of_three(slice, [x, y, z])
}

#[derive(Debug)]
struct Partitioner {
    left_i: usize,
    right_i: usize,
    end_i: usize,
    cursor: usize,
}

impl Partitioner {
    pub const fn new<const C: usize>(len: usize) -> Self {
        Self {
            left_i: C,
            right_i: len - 1 - C,
            end_i: len - 1,
            cursor: 0,
        }
    }

    pub fn next<T: Sortable, const IS_LEFT: bool>(
        &mut self,
        slice: &mut [T],
        swap: Option<&mut [T]>,
        pivot: T,
    ) {
        let val = {
            let i = if IS_LEFT { self.left_i } else { self.right_i };
            let val = swap.map_or_else(
                || {
                    // SAFETY:
                    // `Partitioner::next` is called `slice.len()` times ==> 0 <= i < `slice.len()`
                    unsafe { *slice.get_unchecked(i) }
                },
                |swap| {
                    // SAFETY:
                    // `Partitioner::next` is called `slice.len()` times ==> 0 <= i < `slice.len()` (1)
                    // i < 2 * CRUM_CHUNK_SIZE (2)
                    // 2 * CRUM_CHUNK_SIZE <= `swap.len()` (3)
                    // (1), (2), (3) ==> 0 <= i < `swap.len()`
                    unsafe { *swap.get_unchecked(i) }
                },
            );

            // SAFETY:
            // `usize::from(val <= pivot)` <= 1, `Partitioner::next` is called `slice.len()`
            // times ==> self.cursor <= i` (1)
            // (1), i < `slice.len()` ==> self.cursor < `slice.len()`
            unsafe {
                *slice.get_unchecked_mut(self.cursor) = val;
            }

            // SAFETY:
            // for nth iteration:
            //   `usize::from(val <= pivot)` <= 1 ==> self.cursor <= n (1)
            //   self.end_i = `slice.len()` - 1 - n (2)
            //   (1), (2) ==> self.cursor + self.end_i <= `slice.len()` - 1 <==>
            //   <==> self.cursor + self.end_i < `slice.len()`
            unsafe {
                *slice.get_unchecked_mut(self.cursor + self.end_i) = val;
            }

            val
        };

        if IS_LEFT {
            self.left_i += 1;
        } else {
            self.right_i -= 1;
        }

        self.end_i = self.end_i.overflowing_sub(1).0;
        self.cursor += usize::from(val <= pivot);
    }
}

fn fulcrum_partition_inner<T: Sortable>(slice: &mut [T], swap: &mut [T], pivot: T) -> usize {
    if slice.len() <= swap.len() {
        const CHUNK: usize = 8;

        let mut i = 0;
        let mut cursor = 0;

        let mut partition = |slice: &mut [T]| {
            let val = {
                // SAFETY:
                // `partition` is called `slice.len()` times ==> i < `slice.len()`
                let val = unsafe { *slice.get_unchecked(i) };

                // SAFETY:
                // `swap.len` >= `slice.len()` ==> i < `swap.len()` (1)
                // `usize::from(val <= pivot)` <= 1, `partition` is called `slice.len()` times ==>
                // => cursor <= i (2)
                // (1), (2) ==> 0 <= i - cursor < `swap.len()`
                unsafe {
                    *swap.get_unchecked_mut(i - cursor) = val;
                }

                // SAFETY:
                // cursor <= i, i < `slice.len()` ==> cursor < `slice.len()`
                unsafe {
                    *slice.get_unchecked_mut(cursor) = val;
                }

                val
            };

            i += 1;
            cursor += usize::from(val <= pivot);
        };

        for _ in 0..slice.len() / CHUNK {
            for _ in 0..CHUNK {
                partition(slice);
            }
        }

        for _ in 0..slice.len() % CHUNK {
            partition(slice);
        }

        let len = slice.len();
        slice[cursor..].copy_from_slice(&swap[..len - cursor]);

        return cursor;
    }

    swap[..CRUM_CHUNK_SIZE].copy_from_slice(&slice[..CRUM_CHUNK_SIZE]);
    swap[CRUM_CHUNK_SIZE..2 * CRUM_CHUNK_SIZE]
        .copy_from_slice(&slice[slice.len() - CRUM_CHUNK_SIZE..]);

    let mut partitioner = Partitioner::new::<CRUM_CHUNK_SIZE>(slice.len());

    let mut count = slice.len() / CRUM_CHUNK_SIZE - 2;

    loop {
        if partitioner.left_i - partitioner.cursor <= CRUM_CHUNK_SIZE {
            if let Some(new_count) = count.checked_sub(1) {
                count = new_count;
            } else {
                break;
            }

            for _ in 0..CRUM_CHUNK_SIZE {
                partitioner.next::<_, true>(slice, None, pivot);
            }
        }

        if partitioner.left_i - partitioner.cursor > CRUM_CHUNK_SIZE {
            if let Some(new_count) = count.checked_sub(1) {
                count = new_count;
            } else {
                break;
            }

            for _ in 0..CRUM_CHUNK_SIZE {
                partitioner.next::<_, false>(slice, None, pivot);
            }
        }
    }

    if partitioner.left_i - partitioner.cursor <= CRUM_CHUNK_SIZE {
        for _ in 0..slice.len() % CRUM_CHUNK_SIZE {
            partitioner.next::<_, true>(slice, None, pivot);
        }
    } else {
        for _ in 0..slice.len() % CRUM_CHUNK_SIZE {
            partitioner.next::<_, false>(slice, None, pivot);
        }
    }

    partitioner.left_i = 0;
    for _ in 0..2 * CRUM_CHUNK_SIZE {
        partitioner.next::<_, true>(slice, Some(swap), pivot);
    }

    partitioner.cursor
}

fn fulcrum_partition<T: Sortable + Send>(slice: &mut [T], swap: &mut [T], max: Option<T>) {
    let i = if slice.len() <= 2_048 {
        crum_median_of_nine(slice)
    } else {
        crum_median_of_sqrt(slice, swap)
    };

    let pivot = slice[i];

    if let Some(max_val) = max {
        if max_val <= pivot {
            let left = fulcrum_partition_inner(slice, swap, pivot);
            let right = slice.len() - left;

            if right <= left / 16 || left <= CRUM_OUT {
                return quadsort::quadsort_swap(&mut slice[..left], swap);
            }

            return fulcrum_partition(&mut slice[..left], swap, None);
        }
    }

    let len = slice.len() - 1;

    slice[i] = slice[len];

    let left_i = fulcrum_partition_inner(&mut slice[..len], swap, pivot);
    let right_i = len - left_i;

    slice[len] = slice[left_i];
    slice[left_i] = pivot;

    if left_i <= right_i / 16 || right_i <= CRUM_OUT {
        if right_i == 0 {
            let left = fulcrum_partition_inner(&mut slice[..left_i], swap, pivot);
            let right = len - left;

            if right <= left / 16 || left <= CRUM_OUT {
                return quadsort::quadsort_swap(&mut slice[..left], swap);
            }

            return fulcrum_partition(&mut slice[..left], swap, None);
        }

        quadsort::quadsort_swap(&mut slice[left_i + 1..], swap);
    }

    if slice.len() > JOIN_THRESHOLD {
        let (left, right) = slice.split_at_mut(left_i + 1);

        rayon::join(
            move || {
                if !(left_i <= right_i / 16 || right_i <= CRUM_OUT) {
                    fulcrum_partition(right, swap, max);
                }
            },
            || {
                let mut swap = [T::default(); SWAP_SIZE];

                if right_i <= left_i / 32 || left_i <= CRUM_OUT {
                    return quadsort::quadsort_swap(left, &mut swap);
                }

                let max = Some(left[left.len() - 1]);
                fulcrum_partition(left, &mut swap, max);
            },
        );
    } else {
        if !(left_i <= right_i / 16 || right_i <= CRUM_OUT) {
            fulcrum_partition(&mut slice[left_i + 1..], swap, max);
        }

        if right_i <= left_i / 32 || left_i <= CRUM_OUT {
            return quadsort::quadsort_swap(&mut slice[..left_i], swap);
        }

        let max = Some(slice[left_i]);
        fulcrum_partition(&mut slice[..left_i], swap, max);
    }
}

/// Parallel sort extension trait.
pub trait ParCrumSort {
    /// Unstably sorts the slice.
    ///
    /// # Examples
    ///
    /// ```
    /// use crumsort::ParCrumSort;
    ///
    /// let mut vals = [5, 4, 1, 3, 2];
    ///
    /// vals.par_crumsort();
    ///
    /// assert_eq!(vals, [1, 2, 3, 4, 5]);
    /// ```
    fn par_crumsort(&mut self);
}

impl<T: Copy + Default + Ord + Send> ParCrumSort for [T] {
    fn par_crumsort(&mut self) {
        if self.len() < 32 {
            return quadsort::tail_swap(self);
        }

        fulcrum_partition(self, &mut [T::default(); SWAP_SIZE], None);
    }
}

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

    use rand::prelude::*;

    fn test_partition<const N: usize>(thirds: usize) {
        let mut slice: Vec<_> = (0..N).into_iter().map(|i| N - i - 1).collect();

        let pivot = slice[thirds * slice.len() / 3];
        fulcrum_partition_inner(&mut slice, &mut [0; 2 * CRUM_CHUNK_SIZE], pivot);

        let (left, right) = slice.split_at(pivot + 1);

        assert!(left.iter().all(|&val| val <= pivot));
        assert!(right.iter().all(|&val| val > pivot));
    }

    #[test]
    fn partition7() {
        test_partition::<7>(1);
    }

    #[test]
    fn partition15() {
        test_partition::<15>(1);
    }

    #[test]
    fn partition31() {
        test_partition::<31>(1);
    }

    #[test]
    fn partition159() {
        test_partition::<159>(1);
    }

    #[test]
    fn partition193() {
        test_partition::<193>(2);
    }

    #[test]
    fn sort_16_384_uniform() {
        let mut rng = StdRng::seed_from_u64(42);
        let mut vals: Vec<u32> = (0..16_384).into_iter().map(|_| rng.gen()).collect();
        let mut sorted = vals.clone();

        sorted.sort_unstable();

        vals.as_mut_slice().par_crumsort();

        assert_eq!(vals, sorted);
    }

    #[test]
    fn sort_16_384_uniform_small_range() {
        let mut rng = StdRng::seed_from_u64(42);
        let mut vals: Vec<u32> = (0..16_384)
            .into_iter()
            .map(|_| rng.gen_range(0..64))
            .collect();
        let mut sorted = vals.clone();

        sorted.sort_unstable();

        vals.as_mut_slice().par_crumsort();

        assert_eq!(vals, sorted);
    }
}
<|endoftext|>


<commit_before>//! Variable length signed and unsigned integer types.
//! Types support up to 64-bit integers and encoded to 1-9 bytes (128-bit integers support will be
//! added when Rust has the `i128` and `u128` types, this types will be encoded as 17 bytes).
//!
//! Encoding rules are based on [SQLite 4 Varuint type](https://!sqlite.org/src4/doc/trunk/www/varint.wiki)
//! with modifications for future support of 128-bit long integers.
//! Varint is encoded using the [Protobuf ZigZag approach](https://!developers.google.com/protocol-buffers/docs/encoding#signed-integers)
//! and reuses `Varuint` as a storage.
//!
//! Unlike the Protobuf encoding rules `Varuint` needs the first byte only to find out the length of the
//! whole value. Microbenchmarks say that it is a lot faster.
//!
//! ## How to use
//!
//! Add dependency to your Cargo.toml:
//!
//! ```cargo
//! [dependencies]
//! varuint = "0.3"
//! ```
//!
//! Add imports to your code:
//!
//! ```rust
//! extern crate varuint;
//!
//! use varuint::{Varint, Varuint, Serializable, Deserializable};
//! ```
//!
//! Use it:
//!
//! ```rust
//! use std::mem;
//! use std::io::Read;
//!
//!
//! fn test_varint(v: i64, size: usize) {
//!     let v = Varint(v);
//!     assert_eq!(size, v.size_hint());
//!     let mut arr: [u8; 9] = unsafe { mem::uninitialized() };
//!     {
//!         let mut buf = &mut arr as &mut [u8];
//!         assert_eq!(size, v.serialize(&mut buf).unwrap());
//!     }
//!     let mut buf: &[u8] = &arr;
//!     let mut read: &mut Read = &mut buf;
//!     assert_eq!(v, Varint::deserialize(read).unwrap());
//! }
//!
//! fn main() {
//!     test_varint(0, 1);
//!     test_varint(1, 1);
//!     test_varint(-1, 1);
//! }
//! ```
//!
//! ## Encoding rules
//!
//! Encoding rules for `Varuint` are (assuming value is `V`):
//!
//!   * If `V<=240` then output a single byte `A0` equal to `V`.
//!   * If `V<=2031` then output `A0` as `(V-240)/256 + 241` and `A1` as `(V-240)%256`.
//!   * If `V<=67567` then output `A0` as `248`, `A1` as `(V-2032)/256`, and `A2` as `(V-2032)%256`.
//!   * If `V<=16777215` then output `A0` as `249` and `A1` through `A3` as a big-endian 3-byte integer.
//!   * If `V<=4294967295` then output `A0` as `250` and `A1..A4` as a big-ending 4-byte integer.
//!   * If `V<=1099511627775` then output `A0` as `251` and `A1..A5` as a big-ending 5-byte integer.
//!   * If `V<=281474976710655` then output `A0` as `252` and `A1..A6` as a big-ending 6-byte integer.
//!   * If `V<=72057594037927935` then output `A0` as `253` and `A1..A7` as a big-ending 7-byte integer.
//!   * If `V<=9223372036854775807` then output `A0` as `254` and `A1..A8` as a big-ending 8-byte integer.
//!   * (not supported yet) Otherwise output `A0` as `255` and `A1..A16` as a big-endian 16-byte integer.
//!
//! `Varint` converted to the `Varuint` in the first place and then encoded as unsigned integer.
//! Conversion method makes values closer to 0 take less space.
//! See [Protobuf docs](https://!developers.google.com/protocol-buffers/docs/encoding#signed-integers)
//! for details.
mod varuint;

pub use varuint::{Serializable, Deserializable, Varuint, Varint};<commit_msg>Fix tests<commit_after>//! Variable length signed and unsigned integer types.
//! Types support up to 64-bit integers and encoded to 1-9 bytes (128-bit integers support will be
//! added when Rust has the `i128` and `u128` types, this types will be encoded as 17 bytes).
//!
//! Encoding rules are based on [SQLite 4 Varuint type](https://!sqlite.org/src4/doc/trunk/www/varint.wiki)
//! with modifications for future support of 128-bit long integers.
//! Varint is encoded using the [Protobuf ZigZag approach](https://!developers.google.com/protocol-buffers/docs/encoding#signed-integers)
//! and reuses `Varuint` as a storage.
//!
//! Unlike the Protobuf encoding rules `Varuint` needs the first byte only to find out the length of the
//! whole value. Microbenchmarks say that it is a lot faster.
//!
//! ## How to use
//!
//! Add dependency to your Cargo.toml:
//!
//! ```cargo
//! [dependencies]
//! varuint = "0.3"
//! ```
//!
//! Add imports to your code:
//!
//! ```rust,no_run
//!
//! use varuint::{Varint, Varuint, Serializable, Deserializable};
//! ```
//!
//! Use it:
//!
//! ```rust,no_run
//! use std::mem;
//! use std::io::Read;
//!
//! use varuint::*;
//!
//! fn test_varint(v: i64, size: usize) {
//!     let v = Varint(v);
//!     assert_eq!(size, v.size_hint());
//!     let mut arr: [u8; 9] = unsafe { mem::uninitialized() };
//!     {
//!         let mut buf = &mut arr as &mut [u8];
//!         assert_eq!(size, v.serialize(&mut buf).unwrap());
//!     }
//!     let mut buf: &[u8] = &arr;
//!     let mut read: &mut Read = &mut buf;
//!     assert_eq!(v, Varint::deserialize(read).unwrap());
//! }
//!
//! fn main() {
//!     test_varint(0, 1);
//!     test_varint(1, 1);
//!     test_varint(-1, 1);
//! }
//! ```
//!
//! ## Encoding rules
//!
//! Encoding rules for `Varuint` are (assuming value is `V`):
//!
//!   * If `V<=240` then output a single byte `A0` equal to `V`.
//!   * If `V<=2031` then output `A0` as `(V-240)/256 + 241` and `A1` as `(V-240)%256`.
//!   * If `V<=67567` then output `A0` as `248`, `A1` as `(V-2032)/256`, and `A2` as `(V-2032)%256`.
//!   * If `V<=16777215` then output `A0` as `249` and `A1` through `A3` as a big-endian 3-byte integer.
//!   * If `V<=4294967295` then output `A0` as `250` and `A1..A4` as a big-ending 4-byte integer.
//!   * If `V<=1099511627775` then output `A0` as `251` and `A1..A5` as a big-ending 5-byte integer.
//!   * If `V<=281474976710655` then output `A0` as `252` and `A1..A6` as a big-ending 6-byte integer.
//!   * If `V<=72057594037927935` then output `A0` as `253` and `A1..A7` as a big-ending 7-byte integer.
//!   * If `V<=9223372036854775807` then output `A0` as `254` and `A1..A8` as a big-ending 8-byte integer.
//!   * (not supported yet) Otherwise output `A0` as `255` and `A1..A16` as a big-endian 16-byte integer.
//!
//! `Varint` converted to the `Varuint` in the first place and then encoded as unsigned integer.
//! Conversion method makes values closer to 0 take less space.
//! See [Protobuf docs](https://!developers.google.com/protocol-buffers/docs/encoding#signed-integers)
//! for details.
mod varuint;

pub use varuint::{Serializable, Deserializable, Varuint, Varint};<|endoftext|>


<commit_before>#[macro_use]
extern crate bitflags;
pub mod error;
use error::Result;
use std::str::FromStr;

bitflags! {
    flags ExprType: u64 {
        const TE_VARIABLE = 0,
        const TE_FUNCTION0 = 8,
        const TE_FUNCTION1 = 9,
        const TE_FUNCTION2 = 10,
        const TE_FUNCTION3 = 11,
        const TE_FUNCTION4 = 12,
        const TE_FUNCTION5 = 13,
        const TE_FUNCTION6 = 14,
        const TE_FUNCTION7 = 15,
        const TE_CLOSURE0 = 16,
        const TE_CLOSURE1 = 17,
        const TE_CLOSURE2 = 18,
        const TE_CLOSURE3 = 19,
        const TE_CLOSURE4 = 20,
        const TE_CLOSURE5 = 21,
        const TE_CLOSURE6 = 22,
        const TE_CLOSURE7 = 23,
        const TE_FLAG_PURE = 32,
        const TOK_NULL = 24,
        const TOK_ERROR = 25,
        const TOK_END = 26,
        const TOK_SEP = 27,
        const TOK_OPEN = 28,
        const TOK_CLOSE = 29,
        const TOK_NUMBER = 30,
        const TOK_VARIABLE = 31,
        const TOK_INFIX = 32,
        const T_MASK = 0x0000001F
    }
}

struct Variable {
    pub name: String,
    pub address: i8,
    pub v_type: ExprType,
    pub context: i8,
}

impl Variable {
    fn new() -> Variable {
        Variable {
            name: String::new(),
            address: 0,
            v_type: TE_VARIABLE,
            context: 0,
        }
    }
}

impl Clone for Variable {
    fn clone(&self) -> Variable {
        Variable {
            name: self.name.clone(),
            address: self.address,
            v_type: self.v_type,
            context: self.context
        }
    }
}

struct State {
    pub next: String,
    pub s_type: ExprType,
    pub n_idx: usize,
    pub value: f64,
    pub bound: i8,
    pub function: i8,
    pub context: i8,
    pub lookup: Vec<Variable>,
}

impl State {
    fn new() -> State {
        State {
            next: String::new(),
            s_type: TOK_NULL,
            n_idx: 0,
            value: 0.0,
            bound: 0,
            function: 0,
            context: 0,
            lookup: Vec::<Variable>::new()
        }
    }
}

fn find_lookup(s: &State, txt: &String) -> Option<Variable> {
    for var in &s.lookup {
        if &(*var.name) == txt {
            return Some((*var).clone());
        }
    }

    None
}

fn find_builtin(s: &State, txt: &String) -> Option<Variable> {
    Some(Variable::new())
}

fn next_token(s: &mut State) -> Result<String> {
    s.s_type = TOK_NULL;

    while s.s_type == TOK_NULL {
        if s.n_idx == s.next.len() {
            s.s_type = TOK_END;
            break;
        }

        let next_char = s.next.as_bytes()[s.n_idx] as char;
        // try reading a number
        if (next_char >= '0' && next_char <= '9') || next_char == '.' {
            let mut num_str = String::new();
            let mut c = next_char;

            // extract the number part to separate string which we then convert to f64
            while s.n_idx < s.next.len() && (c >= '0' && c <= '9') || c == '.' {
                num_str.push(c);
                s.n_idx += 1;
                if s.n_idx < s.next.len() {
                    c = s.next.as_bytes()[s.n_idx] as char;
                }
            }
            s.value  = try!(f64::from_str(&num_str));
            s.s_type = TOK_NUMBER;
        } else {
            // look for a variable or builting function call
            if next_char >= 'a' && next_char <= 'z' {
                let mut txt_str = String::new();
                let mut c = next_char;

                while s.n_idx < s.next.len() && (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') {
                    txt_str.push(c);
                    s.n_idx += 1;
                    if s.n_idx < s.next.len() {
                        c = s.next.as_bytes()[s.n_idx] as char;
                    }
                }

                let mut var = find_lookup(&s, &txt_str);
                if let None = var {
                   var = find_builtin(&s, &txt_str);
                }

                if let Some(v) = var {
                    match s.s_type & T_MASK {
                        TE_VARIABLE => { s.s_type = TOK_VARIABLE; s.bound = v.address; },
                        TE_CLOSURE0 => s.context = v.context,
                        TE_CLOSURE1 => s.context = v.context,
                        TE_CLOSURE2 => s.context = v.context,
                        TE_CLOSURE3 => s.context = v.context,
                        TE_CLOSURE4 => s.context = v.context,
                        TE_CLOSURE5 => s.context = v.context,
                        TE_CLOSURE6 => s.context = v.context,
                        TE_CLOSURE7 => s.context = v.context,
                        TE_FUNCTION0 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION1 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION2 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION3 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION4 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION5 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION6 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION7 => { s.s_type = v.v_type; s.function = v.address; },
                        _ => {}
                    }
                }
                else {
                    s.s_type = TOK_ERROR;
                }
            } else {
                // todo: set function
                // look for an operator or special character
                match s.next.as_bytes()[s.n_idx] as char {
                    '+' => s.s_type = TOK_INFIX,
                    '-' => s.s_type = TOK_INFIX,
                    '*' => s.s_type = TOK_INFIX,
                    '/' => s.s_type = TOK_INFIX,
                    '^' => s.s_type = TOK_INFIX,
                    '%' => s.s_type = TOK_INFIX,
                    '(' => s.s_type = TOK_OPEN,
                    ')' => s.s_type = TOK_CLOSE,
                    ',' => s.s_type = TOK_SEP,
                    ' ' | '\t' | '\n' |'\r' => {},
                      _ => s.s_type = TOK_ERROR
                }
                s.n_idx += 1;
            }
        }
    }

    Ok(String::new())
}

pub fn interp(expression: &str) -> Result<String> {
    Ok(String::new())
}

pub fn compile() {
}

pub fn eval() {
}
<commit_msg>implemented find_builtin<commit_after>#[macro_use]
extern crate bitflags;
pub mod error;
use error::Result;
use std::str::FromStr;

bitflags! {
    flags ExprType: u64 {
        const TE_VARIABLE = 0,
        const TE_FUNCTION0 = 8,
        const TE_FUNCTION1 = 9,
        const TE_FUNCTION2 = 10,
        const TE_FUNCTION3 = 11,
        const TE_FUNCTION4 = 12,
        const TE_FUNCTION5 = 13,
        const TE_FUNCTION6 = 14,
        const TE_FUNCTION7 = 15,
        const TE_CLOSURE0 = 16,
        const TE_CLOSURE1 = 17,
        const TE_CLOSURE2 = 18,
        const TE_CLOSURE3 = 19,
        const TE_CLOSURE4 = 20,
        const TE_CLOSURE5 = 21,
        const TE_CLOSURE6 = 22,
        const TE_CLOSURE7 = 23,
        const TE_FLAG_PURE = 32,
        const TOK_NULL = 24,
        const TOK_ERROR = 25,
        const TOK_END = 26,
        const TOK_SEP = 27,
        const TOK_OPEN = 28,
        const TOK_CLOSE = 29,
        const TOK_NUMBER = 30,
        const TOK_VARIABLE = 31,
        const TOK_INFIX = 32,
        const T_MASK = 0x0000001F
    }
}

// todo: function pointers?
const FUNCTIONS: [&'static str; 21] = ["abs", "acos", "asin", "atan", "atan2", "ceil", "cos",
                                       "cosh", "e", "exp", "floor", "ln", "log", "log10",
                                       "pi", "pow", "sin", "sinh", "sqrt", "tan", "tanh" ];
const FUNCTION_TYPES: [ExprType; 21] = [ TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION2, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION0, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION0,
                                         TE_FUNCTION2, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION1];
struct Variable {
    pub name: String,
    pub address: i8,
    pub v_type: ExprType,
    pub context: i8,
}

impl Variable {
    fn new(name: &str, v_type: ExprType) -> Variable {
        Variable {
            name: String::from(name),
            address: 0,
            v_type: v_type,
            context: 0,
        }
    }
}

impl Clone for Variable {
    fn clone(&self) -> Variable {
        Variable {
            name: self.name.clone(),
            address: self.address,
            v_type: self.v_type,
            context: self.context
        }
    }
}

struct State {
    pub next: String,
    pub s_type: ExprType,
    pub n_idx: usize,
    pub value: f64,
    pub bound: i8,
    pub function: i8,
    pub context: i8,
    pub lookup: Vec<Variable>,
}

impl State {
    fn new() -> State {
        State {
            next: String::new(),
            s_type: TOK_NULL,
            n_idx: 0,
            value: 0.0,
            bound: 0,
            function: 0,
            context: 0,
            lookup: Vec::<Variable>::new()
        }
    }
}

fn find_lookup(s: &State, txt: &str) -> Option<Variable> {
    for var in &s.lookup {
        if &(*var.name) == txt {
            return Some((*var).clone());
        }
    }

    None
}

fn find_builtin(txt: &str) -> Option<Variable> {
    if let Ok(idx) = FUNCTIONS.binary_search(&txt) {
        return Some(Variable::new(txt, FUNCTION_TYPES[idx]));
    }
    None
}

fn next_token(s: &mut State) -> Result<String> {
    s.s_type = TOK_NULL;

    while s.s_type == TOK_NULL {
        if s.n_idx == s.next.len() {
            s.s_type = TOK_END;
            break;
        }

        let next_char = s.next.as_bytes()[s.n_idx] as char;
        // try reading a number
        if (next_char >= '0' && next_char <= '9') || next_char == '.' {
            let mut num_str = String::new();
            let mut c = next_char;

            // extract the number part to separate string which we then convert to f64
            while s.n_idx < s.next.len() && (c >= '0' && c <= '9') || c == '.' {
                num_str.push(c);
                s.n_idx += 1;
                if s.n_idx < s.next.len() {
                    c = s.next.as_bytes()[s.n_idx] as char;
                }
            }
            s.value  = try!(f64::from_str(&num_str));
            s.s_type = TOK_NUMBER;
        } else {
            // look for a variable or builting function call
            if next_char >= 'a' && next_char <= 'z' {
                let mut txt_str = String::new();
                let mut c = next_char;

                while s.n_idx < s.next.len() && (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') {
                    txt_str.push(c);
                    s.n_idx += 1;
                    if s.n_idx < s.next.len() {
                        c = s.next.as_bytes()[s.n_idx] as char;
                    }
                }

                let mut var = find_lookup(&s, &txt_str);
                if let None = var {
                   var = find_builtin(&txt_str);
                }

                if let Some(v) = var {
                    match s.s_type & T_MASK {
                        TE_VARIABLE => { s.s_type = TOK_VARIABLE; s.bound = v.address; },
                        TE_CLOSURE0 => s.context = v.context,
                        TE_CLOSURE1 => s.context = v.context,
                        TE_CLOSURE2 => s.context = v.context,
                        TE_CLOSURE3 => s.context = v.context,
                        TE_CLOSURE4 => s.context = v.context,
                        TE_CLOSURE5 => s.context = v.context,
                        TE_CLOSURE6 => s.context = v.context,
                        TE_CLOSURE7 => s.context = v.context,
                        TE_FUNCTION0 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION1 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION2 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION3 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION4 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION5 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION6 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION7 => { s.s_type = v.v_type; s.function = v.address; },
                        _ => {}
                    }
                }
                else {
                    s.s_type = TOK_ERROR;
                }
            } else {
                // todo: set function
                // look for an operator or special character
                match s.next.as_bytes()[s.n_idx] as char {
                    '+' => s.s_type = TOK_INFIX,
                    '-' => s.s_type = TOK_INFIX,
                    '*' => s.s_type = TOK_INFIX,
                    '/' => s.s_type = TOK_INFIX,
                    '^' => s.s_type = TOK_INFIX,
                    '%' => s.s_type = TOK_INFIX,
                    '(' => s.s_type = TOK_OPEN,
                    ')' => s.s_type = TOK_CLOSE,
                    ',' => s.s_type = TOK_SEP,
                    ' ' | '\t' | '\n' |'\r' => {},
                      _ => s.s_type = TOK_ERROR
                }
                s.n_idx += 1;
            }
        }
    }

    Ok(String::new())
}

pub fn interp(expression: &str) -> Result<String> {
    Ok(String::new())
}

pub fn compile() {
}

pub fn eval() {
}
<|endoftext|>