<commit_before>use std::{
    cell::Cell,
    collections::{HashMap, HashSet},
};

use super::behaviors::MouseBehavior;
use crate::{api::prelude::*, prelude::*, proc_macros::*, theme::prelude::*};

static ITEMS_PANEL: &str = "items_panel";

/// The `ListViewState` generates the list box items and handles the selected indices.
#[derive(Default, AsAny)]
pub struct ListViewState {
    builder: WidgetBuildContext,
    count: usize,
    selected_entities: RefCell<HashSet<Entity>>,
    items_panel: Entity,
}

impl ListViewState {
    fn generate_items(&mut self, ctx: &mut Context) {
        let count = ctx.widget().clone_or_default::<usize>("count");
        let entity = ctx.entity;

        if count != self.count || *ctx.widget().get::<bool>("request_update") {
            ctx.widget().set("request_update", false);
            if let Some(builder) = &self.builder {
                ctx.clear_children_of(self.items_panel);

                for i in 0..count {
                    let item = {
                        let build_context = &mut ctx.build_context();
                        let child = builder(build_context, i);
                        let item = ListViewItem::new().parent(entity.0).build(build_context);

                        let mouse_behavior =
                            MouseBehavior::new().target(item.0).build(build_context);
                        build_context.register_shared_property::<Selector>(
                            "selector",
                            mouse_behavior,
                            item,
                        );
                        build_context.register_shared_property::<bool>(
                            "pressed",
                            mouse_behavior,
                            item,
                        );
                        build_context.append_child(item, mouse_behavior);

                        build_context.register_shared_property::<Brush>("foreground", child, item);
                        build_context.register_shared_property::<f32>("opacity", item, entity);
                        build_context.register_shared_property::<f32>("opacity", child, entity);
                        build_context.register_shared_property::<f64>("font_size", child, item);
                        build_context.append_child(self.items_panel, item);
                        build_context.append_child(mouse_behavior, child);

                        item
                    };
                    ctx.get_widget(item).update_widget(entity, false, false);
                }
            }

            self.count = count;
        }
    }
}

impl State for ListViewState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.items_panel = ctx
            .entity_of_child(ITEMS_PANEL)
            .expect("ListViewState.init: ItemsPanel child could not be found.");

        self.generate_items(ctx);
    }

    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.generate_items(ctx);
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context) {
        for index in ctx
            .widget()
            .get::<SelectedEntities>("selected_entities")
            .0
            .clone()
            .symmetric_difference(&*self.selected_entities.borrow())
        {
            let mut widget = ctx.get_widget(*index);

            if !widget.has::<bool>("selected") {
                continue;
            }

            let selected = !widget.get::<bool>("selected");
            widget.set("selected", selected);

            if selected {
                widget.get_mut::<Selector>("selector").set_state("selected");
            } else {
                widget.get_mut::<Selector>("selector").clear_state();
            }

            widget.update(false);
        }

        *self.selected_entities.borrow_mut() = ctx
            .widget()
            .get::<SelectedEntities>("selected_entities")
            .0
            .clone();
    }
}

/// The `ListViewItemState` handles the interaction and selection of a `ListViewItem`.
#[derive(Default, AsAny)]
pub struct ListViewItemState {
    request_selection_toggle: Cell<bool>,
}

impl ListViewItemState {
    fn toggle_selection(&self) {
        self.request_selection_toggle.set(true);
    }
}

impl State for ListViewItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
            return;
        }
        self.request_selection_toggle.set(false);

        let selected = *ctx.widget().get::<bool>("selected");

        let entity = ctx.entity;
        let index = ctx.index_as_child(entity).unwrap();

        let parent_entity: Entity = (*ctx.widget().get::<u32>("parent")).into();

        let mut parent = ctx.get_widget(parent_entity);

        let selection_mode = *parent.get::<SelectionMode>("selection_mode");
        // deselect item
        if selected {
            parent
                .get_mut::<SelectedEntities>("selected_entities")
                .0
                .remove(&entity);
            parent
                .get_mut::<SelectedIndices>("selected_indices")
                .0
                .remove(&index);
            return;
        }

        if parent
            .get::<SelectedEntities>("selected_entities")
            .0
            .contains(&entity)
            || selection_mode == SelectionMode::None
        {
            return;
        }

        if selection_mode == SelectionMode::Single {
            parent
                .get_mut::<SelectedEntities>("selected_entities")
                .0
                .clear();
            parent
                .get_mut::<SelectedIndices>("selected_indices")
                .0
                .clear();
        }

        parent
            .get_mut::<SelectedEntities>("selected_entities")
            .0
            .insert(entity);
        parent
            .get_mut::<SelectedIndices>("selected_indices")
            .0
            .insert(index);

        let selected_indices: Vec<usize> = parent
            .get::<SelectedIndices>("selected_indices")
            .0
            .iter()
            .copied()
            .collect();

        ctx.push_event_strategy_by_entity(
            SelectionChangedEvent(parent_entity, selected_indices),
            parent_entity,
            EventStrategy::Direct,
        );
    }
}

widget!(
    /// The `ListViewItem` describes an item inside of a `ListView`.
    ///
    /// **style:** `list-view``
    ListViewItem<ListViewItemState>: MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the selected property.
        selected: bool,

        /// Sets or shares the parent id.
        parent: u32
    }
);

impl Template for ListViewItem {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        self.name("ListViewItem")
            .style("list_view_item")
            .min_width(64.0)
            .height(24.0)
            .selected(false)
            .pressed(false)
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .font_size(32.0)
            .font("Roboto-Regular")
            .on_click(move |states, _| {
                states.get::<ListViewItemState>(id).toggle_selection();
                false
            })
            .child(
                MouseBehavior::new()
                    .pressed(id)
                    .enabled(id)
                    .target(id.0)
                    .build(ctx),
            )
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        RectangleRenderObject.into()
    }

    fn layout(&self) -> Box<dyn Layout> {
        PaddingLayout::new().into()
    }
}

widget!(
    /// The `ListView` is an items drawer widget with selectable items.
    ///
    /// **style:** `items-widget`
    ListView<ListViewState> : SelectionChangedHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the orientation property.
        orientation: Orientation,

        /// Sets or shares the item count.
        count: usize,

        /// Sets or shares the selection mode property.
        selection_mode: SelectionMode,

        /// Sets or shares the selected indices.
        selected_indices: SelectedIndices,

        /// Sets or shares the list of selected indices.
        selected_entities: SelectedEntities,

        /// Use this flag to force the redrawing of the items.
        request_update: bool
    }
);

impl ListView {
    /// Define the template build function for the content of the ListViewItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Box::new(builder));
        self
    }
}

impl Template for ListView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let items_panel = Stack::new()
            .v_align("start")
            .id(ITEMS_PANEL)
            .orientation(id)
            .build(ctx);

        let scroll_viewer = ScrollViewer::new()
            .mode(("disabled", "auto"))
            .child(items_panel)
            .build(ctx);

        self.name("ListView")
            .style("list_view")
            .background(colors::LYNCH_COLOR)
            .border_radius(2.0)
            .border_width(1.0)
            .border_brush(colors::BOMBAY_COLOR)
            .padding(2.0)
            .selection_mode("single")
            .selected_indices(HashSet::new())
            .selected_entities(HashSet::new())
            .orientation("vertical")
            .child(
                Container::new()
                    .background(id)
                    .border_radius(id)
                    .border_width(id)
                    .border_brush(id)
                    .padding(id)
                    .opacity(id)
                    .child(scroll_viewer)
                    .child(
                        ScrollIndicator::new()
                            .padding(2.0)
                            .content_bounds(("bounds", items_panel))
                            .view_port_bounds(("bounds", scroll_viewer))
                            .scroll_padding(("padding", scroll_viewer))
                            .mode(scroll_viewer)
                            .opacity(id)
                            .build(ctx),
                    )
                    .build(ctx),
            )
    }
}
<commit_msg>list view item font fix<commit_after>use std::{
    cell::Cell,
    collections::{HashMap, HashSet},
};

use super::behaviors::MouseBehavior;
use crate::{api::prelude::*, prelude::*, proc_macros::*, theme::prelude::*};

static ITEMS_PANEL: &str = "items_panel";

/// The `ListViewState` generates the list box items and handles the selected indices.
#[derive(Default, AsAny)]
pub struct ListViewState {
    builder: WidgetBuildContext,
    count: usize,
    selected_entities: RefCell<HashSet<Entity>>,
    items_panel: Entity,
}

impl ListViewState {
    fn generate_items(&mut self, ctx: &mut Context) {
        let count = ctx.widget().clone_or_default::<usize>("count");
        let entity = ctx.entity;

        if count != self.count || *ctx.widget().get::<bool>("request_update") {
            ctx.widget().set("request_update", false);
            if let Some(builder) = &self.builder {
                ctx.clear_children_of(self.items_panel);

                for i in 0..count {
                    let item = {
                        let build_context = &mut ctx.build_context();
                        let child = builder(build_context, i);
                        let item = ListViewItem::new().parent(entity.0).build(build_context);

                        let mouse_behavior =
                            MouseBehavior::new().target(item.0).build(build_context);
                        build_context.register_shared_property::<Selector>(
                            "selector",
                            mouse_behavior,
                            item,
                        );
                        build_context.register_shared_property::<bool>(
                            "pressed",
                            mouse_behavior,
                            item,
                        );
                        build_context.append_child(item, mouse_behavior);

                        build_context.register_shared_property::<Brush>("foreground", child, item);
                        build_context.register_shared_property::<f32>("opacity", item, entity);
                        build_context.register_shared_property::<f32>("opacity", child, entity);
                        build_context.register_shared_property::<f64>("font_size", child, item);
                        build_context.register_shared_property::<f64>("font", child, item);
                        build_context.append_child(self.items_panel, item);
                        build_context.append_child(mouse_behavior, child);

                        item
                    };
                    ctx.get_widget(item).update_widget(entity, false, false);
                }
            }

            self.count = count;
        }
    }
}

impl State for ListViewState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.items_panel = ctx
            .entity_of_child(ITEMS_PANEL)
            .expect("ListViewState.init: ItemsPanel child could not be found.");

        self.generate_items(ctx);
    }

    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.generate_items(ctx);
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context) {
        for index in ctx
            .widget()
            .get::<SelectedEntities>("selected_entities")
            .0
            .clone()
            .symmetric_difference(&*self.selected_entities.borrow())
        {
            let mut widget = ctx.get_widget(*index);

            if !widget.has::<bool>("selected") {
                continue;
            }

            let selected = !widget.get::<bool>("selected");
            widget.set("selected", selected);

            if selected {
                widget.get_mut::<Selector>("selector").set_state("selected");
            } else {
                widget.get_mut::<Selector>("selector").clear_state();
            }

            widget.update(false);
        }

        *self.selected_entities.borrow_mut() = ctx
            .widget()
            .get::<SelectedEntities>("selected_entities")
            .0
            .clone();
    }
}

/// The `ListViewItemState` handles the interaction and selection of a `ListViewItem`.
#[derive(Default, AsAny)]
pub struct ListViewItemState {
    request_selection_toggle: Cell<bool>,
}

impl ListViewItemState {
    fn toggle_selection(&self) {
        self.request_selection_toggle.set(true);
    }
}

impl State for ListViewItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
            return;
        }
        self.request_selection_toggle.set(false);

        let selected = *ctx.widget().get::<bool>("selected");

        let entity = ctx.entity;
        let index = ctx.index_as_child(entity).unwrap();

        let parent_entity: Entity = (*ctx.widget().get::<u32>("parent")).into();

        let mut parent = ctx.get_widget(parent_entity);

        let selection_mode = *parent.get::<SelectionMode>("selection_mode");
        // deselect item
        if selected {
            parent
                .get_mut::<SelectedEntities>("selected_entities")
                .0
                .remove(&entity);
            parent
                .get_mut::<SelectedIndices>("selected_indices")
                .0
                .remove(&index);
            return;
        }

        if parent
            .get::<SelectedEntities>("selected_entities")
            .0
            .contains(&entity)
            || selection_mode == SelectionMode::None
        {
            return;
        }

        if selection_mode == SelectionMode::Single {
            parent
                .get_mut::<SelectedEntities>("selected_entities")
                .0
                .clear();
            parent
                .get_mut::<SelectedIndices>("selected_indices")
                .0
                .clear();
        }

        parent
            .get_mut::<SelectedEntities>("selected_entities")
            .0
            .insert(entity);
        parent
            .get_mut::<SelectedIndices>("selected_indices")
            .0
            .insert(index);

        let selected_indices: Vec<usize> = parent
            .get::<SelectedIndices>("selected_indices")
            .0
            .iter()
            .copied()
            .collect();

        ctx.push_event_strategy_by_entity(
            SelectionChangedEvent(parent_entity, selected_indices),
            parent_entity,
            EventStrategy::Direct,
        );
    }
}

widget!(
    /// The `ListViewItem` describes an item inside of a `ListView`.
    ///
    /// **style:** `list-view``
    ListViewItem<ListViewItemState>: MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the selected property.
        selected: bool,

        /// Sets or shares the parent id.
        parent: u32
    }
);

impl Template for ListViewItem {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        self.name("ListViewItem")
            .style("list_view_item")
            .min_width(64.0)
            .height(24.0)
            .selected(false)
            .pressed(false)
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .font_size(32.0)
            .font("Roboto-Regular")
            .on_click(move |states, _| {
                states.get::<ListViewItemState>(id).toggle_selection();
                false
            })
            .child(
                MouseBehavior::new()
                    .pressed(id)
                    .enabled(id)
                    .target(id.0)
                    .build(ctx),
            )
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        RectangleRenderObject.into()
    }

    fn layout(&self) -> Box<dyn Layout> {
        PaddingLayout::new().into()
    }
}

widget!(
    /// The `ListView` is an items drawer widget with selectable items.
    ///
    /// **style:** `items-widget`
    ListView<ListViewState> : SelectionChangedHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the orientation property.
        orientation: Orientation,

        /// Sets or shares the item count.
        count: usize,

        /// Sets or shares the selection mode property.
        selection_mode: SelectionMode,

        /// Sets or shares the selected indices.
        selected_indices: SelectedIndices,

        /// Sets or shares the list of selected indices.
        selected_entities: SelectedEntities,

        /// Use this flag to force the redrawing of the items.
        request_update: bool
    }
);

impl ListView {
    /// Define the template build function for the content of the ListViewItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Box::new(builder));
        self
    }
}

impl Template for ListView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let items_panel = Stack::new()
            .v_align("start")
            .id(ITEMS_PANEL)
            .orientation(id)
            .build(ctx);

        let scroll_viewer = ScrollViewer::new()
            .mode(("disabled", "auto"))
            .child(items_panel)
            .build(ctx);

        self.name("ListView")
            .style("list_view")
            .background(colors::LYNCH_COLOR)
            .border_radius(2.0)
            .border_width(1.0)
            .border_brush(colors::BOMBAY_COLOR)
            .padding(2.0)
            .selection_mode("single")
            .selected_indices(HashSet::new())
            .selected_entities(HashSet::new())
            .orientation("vertical")
            .child(
                Container::new()
                    .background(id)
                    .border_radius(id)
                    .border_width(id)
                    .border_brush(id)
                    .padding(id)
                    .opacity(id)
                    .child(scroll_viewer)
                    .child(
                        ScrollIndicator::new()
                            .padding(2.0)
                            .content_bounds(("bounds", items_panel))
                            .view_port_bounds(("bounds", scroll_viewer))
                            .scroll_padding(("padding", scroll_viewer))
                            .mode(scroll_viewer)
                            .opacity(id)
                            .build(ctx),
                    )
                    .build(ctx),
            )
    }
}
<|endoftext|>


<commit_before>use crossbeam_skiplist::SkipSet;
use crossbeam_utils::thread;
use std::{iter, sync::Barrier};

#[test]
fn smoke() {
    let m = SkipSet::new();
    m.insert(1);
    m.insert(5);
    m.insert(7);
}

// https://github.com/crossbeam-rs/crossbeam/issues/672
#[test]
fn concurrent_insert() {
    for _ in 0..100 {
        let set: SkipSet<i32> = iter::once(1).collect();
        let barrier = Barrier::new(2);
        thread::scope(|s| {
            s.spawn(|_| {
                barrier.wait();
                set.insert(1);
            });
            s.spawn(|_| {
                barrier.wait();
                set.insert(1);
            });
        })
        .unwrap();
    }
}

// https://github.com/crossbeam-rs/crossbeam/issues/672
#[test]
fn concurrent_remove() {
    for _ in 0..100 {
        let set: SkipSet<i32> = iter::once(1).collect();
        let barrier = Barrier::new(2);
        thread::scope(|s| {
            s.spawn(|_| {
                barrier.wait();
                set.remove(&1);
            });
            s.spawn(|_| {
                barrier.wait();
                set.remove(&1);
            });
        })
        .unwrap();
    }
}

#[test]
fn iter() {
    let s = SkipSet::new();
    for &x in &[4, 2, 12, 8, 7, 11, 5] {
        s.insert(x);
    }

    assert_eq!(
        s.iter().map(|e| *e).collect::<Vec<_>>(),
        &[2, 4, 5, 7, 8, 11, 12]
    );

    let mut it = s.iter();
    s.remove(&2);
    assert_eq!(*it.next().unwrap(), 4);
    s.remove(&7);
    assert_eq!(*it.next().unwrap(), 5);
    s.remove(&5);
    assert_eq!(*it.next().unwrap(), 8);
    s.remove(&12);
    assert_eq!(*it.next().unwrap(), 11);
    assert!(it.next().is_none());
}

// https://github.com/crossbeam-rs/crossbeam/issues/671
#[test]
fn iter_range() {
    let set: SkipSet<_> = [1, 3, 5].iter().cloned().collect();
    assert_eq!(set.range(2..4).count(), 1);
    set.insert(3);
}
<commit_msg>Add more tests for SkipSet<commit_after>use crossbeam_skiplist::SkipSet;
use crossbeam_utils::thread;
use std::{iter, ops::Bound, sync::Barrier};

#[test]
fn smoke() {
    let m = SkipSet::new();
    m.insert(1);
    m.insert(5);
    m.insert(7);
}

#[test]
fn is_empty() {
    let s = SkipSet::new();
    assert!(s.is_empty());

    s.insert(1);
    assert!(!s.is_empty());
    s.insert(2);
    s.insert(3);
    assert!(!s.is_empty());

    s.remove(&2);
    assert!(!s.is_empty());

    s.remove(&1);
    assert!(!s.is_empty());

    s.remove(&3);
    assert!(s.is_empty());
}

#[test]
fn insert() {
    let insert = [0, 4, 2, 12, 8, 7, 11, 5];
    let not_present = [1, 3, 6, 9, 10];
    let s = SkipSet::new();

    for &x in &insert {
        s.insert(x);
        assert_eq!(*s.get(&x).unwrap(), x);
    }

    for &x in &not_present {
        assert!(s.get(&x).is_none());
    }
}

#[test]
fn remove() {
    let insert = [0, 4, 2, 12, 8, 7, 11, 5];
    let not_present = [1, 3, 6, 9, 10];
    let remove = [2, 12, 8];
    let remaining = [0, 4, 5, 7, 11];

    let s = SkipSet::new();

    for &x in &insert {
        s.insert(x);
    }
    for x in &not_present {
        assert!(s.remove(x).is_none());
    }
    for x in &remove {
        assert!(s.remove(x).is_some());
    }

    let mut v = vec![];
    let mut e = s.front().unwrap();
    loop {
        v.push(*e);
        if !e.move_next() {
            break;
        }
    }

    assert_eq!(v, remaining);
    for x in &insert {
        s.remove(x);
    }
    assert!(s.is_empty());
}

// https://github.com/crossbeam-rs/crossbeam/issues/672
#[test]
fn concurrent_insert() {
    for _ in 0..100 {
        let set: SkipSet<i32> = iter::once(1).collect();
        let barrier = Barrier::new(2);
        thread::scope(|s| {
            s.spawn(|_| {
                barrier.wait();
                set.insert(1);
            });
            s.spawn(|_| {
                barrier.wait();
                set.insert(1);
            });
        })
        .unwrap();
    }
}

// https://github.com/crossbeam-rs/crossbeam/issues/672
#[test]
fn concurrent_remove() {
    for _ in 0..100 {
        let set: SkipSet<i32> = iter::once(1).collect();
        let barrier = Barrier::new(2);
        thread::scope(|s| {
            s.spawn(|_| {
                barrier.wait();
                set.remove(&1);
            });
            s.spawn(|_| {
                barrier.wait();
                set.remove(&1);
            });
        })
        .unwrap();
    }
}

#[test]
fn entry() {
    let s = SkipSet::new();

    assert!(s.front().is_none());
    assert!(s.back().is_none());

    for &x in &[4, 2, 12, 8, 7, 11, 5] {
        s.insert(x);
    }

    let mut e = s.front().unwrap();
    assert_eq!(*e, 2);
    assert!(!e.move_prev());
    assert!(e.move_next());
    assert_eq!(*e, 4);

    e = s.back().unwrap();
    assert_eq!(*e, 12);
    assert!(!e.move_next());
    assert!(e.move_prev());
    assert_eq!(*e, 11);
}

#[test]
fn entry_remove() {
    let s = SkipSet::new();

    for &x in &[4, 2, 12, 8, 7, 11, 5] {
        s.insert(x);
    }

    let mut e = s.get(&7).unwrap();
    assert!(!e.is_removed());
    assert!(e.remove());
    assert!(e.is_removed());

    e.move_prev();
    e.move_next();
    assert_ne!(*e, 7);

    for e in s.iter() {
        assert!(!s.is_empty());
        assert_ne!(s.len(), 0);
        e.remove();
    }
    assert!(s.is_empty());
    assert_eq!(s.len(), 0);
}

#[test]
fn entry_reposition() {
    let s = SkipSet::new();

    for &x in &[4, 2, 12, 8, 7, 11, 5] {
        s.insert(x);
    }

    let mut e = s.get(&7).unwrap();
    assert!(!e.is_removed());
    assert!(e.remove());
    assert!(e.is_removed());

    s.insert(7);
    e.move_prev();
    e.move_next();
    assert_eq!(*e, 7);
}

#[test]
fn len() {
    let s = SkipSet::new();
    assert_eq!(s.len(), 0);

    for (i, &x) in [4, 2, 12, 8, 7, 11, 5].iter().enumerate() {
        s.insert(x);
        assert_eq!(s.len(), i + 1);
    }

    s.insert(5);
    assert_eq!(s.len(), 7);
    s.insert(5);
    assert_eq!(s.len(), 7);

    s.remove(&6);
    assert_eq!(s.len(), 7);
    s.remove(&5);
    assert_eq!(s.len(), 6);
    s.remove(&12);
    assert_eq!(s.len(), 5);
}

#[test]
fn insert_and_remove() {
    let s = SkipSet::new();
    let keys = || s.iter().map(|e| *e).collect::<Vec<_>>();

    s.insert(3);
    s.insert(5);
    s.insert(1);
    s.insert(4);
    s.insert(2);
    assert_eq!(keys(), [1, 2, 3, 4, 5]);

    assert!(s.remove(&4).is_some());
    assert_eq!(keys(), [1, 2, 3, 5]);
    assert!(s.remove(&3).is_some());
    assert_eq!(keys(), [1, 2, 5]);
    assert!(s.remove(&1).is_some());
    assert_eq!(keys(), [2, 5]);

    assert!(s.remove(&1).is_none());
    assert_eq!(keys(), [2, 5]);
    assert!(s.remove(&3).is_none());
    assert_eq!(keys(), [2, 5]);

    assert!(s.remove(&2).is_some());
    assert_eq!(keys(), [5]);
    assert!(s.remove(&5).is_some());
    assert_eq!(keys(), []);

    s.insert(3);
    assert_eq!(keys(), [3]);
    s.insert(1);
    assert_eq!(keys(), [1, 3]);
    s.insert(3);
    assert_eq!(keys(), [1, 3]);
    s.insert(5);
    assert_eq!(keys(), [1, 3, 5]);

    assert!(s.remove(&3).is_some());
    assert_eq!(keys(), [1, 5]);
    assert!(s.remove(&1).is_some());
    assert_eq!(keys(), [5]);
    assert!(s.remove(&3).is_none());
    assert_eq!(keys(), [5]);
    assert!(s.remove(&5).is_some());
    assert_eq!(keys(), []);
}

#[test]
fn get() {
    let s = SkipSet::new();
    s.insert(30);
    s.insert(50);
    s.insert(10);
    s.insert(40);
    s.insert(20);

    assert_eq!(*s.get(&10).unwrap(), 10);
    assert_eq!(*s.get(&20).unwrap(), 20);
    assert_eq!(*s.get(&30).unwrap(), 30);
    assert_eq!(*s.get(&40).unwrap(), 40);
    assert_eq!(*s.get(&50).unwrap(), 50);

    assert!(s.get(&7).is_none());
    assert!(s.get(&27).is_none());
    assert!(s.get(&31).is_none());
    assert!(s.get(&97).is_none());
}

#[test]
fn lower_bound() {
    let s = SkipSet::new();
    s.insert(30);
    s.insert(50);
    s.insert(10);
    s.insert(40);
    s.insert(20);

    assert_eq!(*s.lower_bound(Bound::Unbounded).unwrap(), 10);

    assert_eq!(*s.lower_bound(Bound::Included(&10)).unwrap(), 10);
    assert_eq!(*s.lower_bound(Bound::Included(&20)).unwrap(), 20);
    assert_eq!(*s.lower_bound(Bound::Included(&30)).unwrap(), 30);
    assert_eq!(*s.lower_bound(Bound::Included(&40)).unwrap(), 40);
    assert_eq!(*s.lower_bound(Bound::Included(&50)).unwrap(), 50);

    assert_eq!(*s.lower_bound(Bound::Included(&7)).unwrap(), 10);
    assert_eq!(*s.lower_bound(Bound::Included(&27)).unwrap(), 30);
    assert_eq!(*s.lower_bound(Bound::Included(&31)).unwrap(), 40);
    assert!(s.lower_bound(Bound::Included(&97)).is_none());

    assert_eq!(*s.lower_bound(Bound::Excluded(&10)).unwrap(), 20);
    assert_eq!(*s.lower_bound(Bound::Excluded(&20)).unwrap(), 30);
    assert_eq!(*s.lower_bound(Bound::Excluded(&30)).unwrap(), 40);
    assert_eq!(*s.lower_bound(Bound::Excluded(&40)).unwrap(), 50);
    assert!(s.lower_bound(Bound::Excluded(&50)).is_none());

    assert_eq!(*s.lower_bound(Bound::Excluded(&7)).unwrap(), 10);
    assert_eq!(*s.lower_bound(Bound::Excluded(&27)).unwrap(), 30);
    assert_eq!(*s.lower_bound(Bound::Excluded(&31)).unwrap(), 40);
    assert!(s.lower_bound(Bound::Excluded(&97)).is_none());
}

#[test]
fn upper_bound() {
    let s = SkipSet::new();
    s.insert(30);
    s.insert(50);
    s.insert(10);
    s.insert(40);
    s.insert(20);

    assert_eq!(*s.upper_bound(Bound::Unbounded).unwrap(), 50);

    assert_eq!(*s.upper_bound(Bound::Included(&10)).unwrap(), 10);
    assert_eq!(*s.upper_bound(Bound::Included(&20)).unwrap(), 20);
    assert_eq!(*s.upper_bound(Bound::Included(&30)).unwrap(), 30);
    assert_eq!(*s.upper_bound(Bound::Included(&40)).unwrap(), 40);
    assert_eq!(*s.upper_bound(Bound::Included(&50)).unwrap(), 50);

    assert!(s.upper_bound(Bound::Included(&7)).is_none());
    assert_eq!(*s.upper_bound(Bound::Included(&27)).unwrap(), 20);
    assert_eq!(*s.upper_bound(Bound::Included(&31)).unwrap(), 30);
    assert_eq!(*s.upper_bound(Bound::Included(&97)).unwrap(), 50);

    assert!(s.upper_bound(Bound::Excluded(&10)).is_none());
    assert_eq!(*s.upper_bound(Bound::Excluded(&20)).unwrap(), 10);
    assert_eq!(*s.upper_bound(Bound::Excluded(&30)).unwrap(), 20);
    assert_eq!(*s.upper_bound(Bound::Excluded(&40)).unwrap(), 30);
    assert_eq!(*s.upper_bound(Bound::Excluded(&50)).unwrap(), 40);

    assert!(s.upper_bound(Bound::Excluded(&7)).is_none());
    assert_eq!(*s.upper_bound(Bound::Excluded(&27)).unwrap(), 20);
    assert_eq!(*s.upper_bound(Bound::Excluded(&31)).unwrap(), 30);
    assert_eq!(*s.upper_bound(Bound::Excluded(&97)).unwrap(), 50);
}

#[test]
fn get_or_insert() {
    let s = SkipSet::new();
    s.insert(3);
    s.insert(5);
    s.insert(1);
    s.insert(4);
    s.insert(2);

    assert_eq!(*s.get(&4).unwrap(), 4);
    assert_eq!(*s.insert(4), 4);
    assert_eq!(*s.get(&4).unwrap(), 4);

    assert_eq!(*s.get_or_insert(4), 4);
    assert_eq!(*s.get(&4).unwrap(), 4);
    assert_eq!(*s.get_or_insert(6), 6);
}

#[test]
fn get_next_prev() {
    let s = SkipSet::new();
    s.insert(3);
    s.insert(5);
    s.insert(1);
    s.insert(4);
    s.insert(2);

    let mut e = s.get(&3).unwrap();
    assert_eq!(*e.next().unwrap(), 4);
    assert_eq!(*e.prev().unwrap(), 2);
    assert_eq!(*e, 3);

    e.move_prev();
    assert_eq!(*e.next().unwrap(), 3);
    assert_eq!(*e.prev().unwrap(), 1);
    assert_eq!(*e, 2);

    e.move_prev();
    assert_eq!(*e.next().unwrap(), 2);
    assert!(e.prev().is_none());
    assert_eq!(*e, 1);

    e.move_next();
    e.move_next();
    e.move_next();
    e.move_next();
    assert!(e.next().is_none());
    assert_eq!(*e.prev().unwrap(), 4);
    assert_eq!(*e, 5);
}

#[test]
fn front_and_back() {
    let s = SkipSet::new();
    assert!(s.front().is_none());
    assert!(s.back().is_none());

    for &x in &[4, 2, 12, 8, 7, 11, 5] {
        s.insert(x);
    }

    assert_eq!(*s.front().unwrap(), 2);
    assert_eq!(*s.back().unwrap(), 12);
}

#[test]
fn iter() {
    let s = SkipSet::new();
    for &x in &[4, 2, 12, 8, 7, 11, 5] {
        s.insert(x);
    }

    assert_eq!(
        s.iter().map(|e| *e).collect::<Vec<_>>(),
        &[2, 4, 5, 7, 8, 11, 12]
    );

    let mut it = s.iter();
    s.remove(&2);
    assert_eq!(*it.next().unwrap(), 4);
    s.remove(&7);
    assert_eq!(*it.next().unwrap(), 5);
    s.remove(&5);
    assert_eq!(*it.next().unwrap(), 8);
    s.remove(&12);
    assert_eq!(*it.next().unwrap(), 11);
    assert!(it.next().is_none());
}

#[test]
fn iter_range() {
    use std::ops::Bound::*;
    let s = SkipSet::new();
    let v = (0..10).map(|x| x * 10).collect::<Vec<_>>();
    for &x in v.iter() {
        s.insert(x);
    }

    assert_eq!(
        s.iter().map(|x| *x).collect::<Vec<_>>(),
        vec![0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
    );
    assert_eq!(
        s.iter().rev().map(|x| *x).collect::<Vec<_>>(),
        vec![90, 80, 70, 60, 50, 40, 30, 20, 10, 0]
    );
    assert_eq!(
        s.range(..).map(|x| *x).collect::<Vec<_>>(),
        vec![0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
    );

    assert_eq!(
        s.range((Included(&0), Unbounded))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
    );
    assert_eq!(
        s.range((Excluded(&0), Unbounded))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![10, 20, 30, 40, 50, 60, 70, 80, 90]
    );
    assert_eq!(
        s.range((Included(&25), Unbounded))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![30, 40, 50, 60, 70, 80, 90]
    );
    assert_eq!(
        s.range((Excluded(&25), Unbounded))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![30, 40, 50, 60, 70, 80, 90]
    );
    assert_eq!(
        s.range((Included(&70), Unbounded))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![70, 80, 90]
    );
    assert_eq!(
        s.range((Excluded(&70), Unbounded))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![80, 90]
    );
    assert_eq!(
        s.range((Included(&100), Unbounded))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Excluded(&100), Unbounded))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );

    assert_eq!(
        s.range((Unbounded, Included(&90)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
    );
    assert_eq!(
        s.range((Unbounded, Excluded(&90)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![0, 10, 20, 30, 40, 50, 60, 70, 80]
    );
    assert_eq!(
        s.range((Unbounded, Included(&25)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![0, 10, 20]
    );
    assert_eq!(
        s.range((Unbounded, Excluded(&25)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![0, 10, 20]
    );
    assert_eq!(
        s.range((Unbounded, Included(&70)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![0, 10, 20, 30, 40, 50, 60, 70]
    );
    assert_eq!(
        s.range((Unbounded, Excluded(&70)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![0, 10, 20, 30, 40, 50, 60]
    );
    assert_eq!(
        s.range((Unbounded, Included(&-1)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Unbounded, Excluded(&-1)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );

    assert_eq!(
        s.range((Included(&25), Included(&80)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![30, 40, 50, 60, 70, 80]
    );
    assert_eq!(
        s.range((Included(&25), Excluded(&80)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![30, 40, 50, 60, 70]
    );
    assert_eq!(
        s.range((Excluded(&25), Included(&80)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![30, 40, 50, 60, 70, 80]
    );
    assert_eq!(
        s.range((Excluded(&25), Excluded(&80)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![30, 40, 50, 60, 70]
    );

    assert_eq!(
        s.range((Included(&25), Included(&25)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Included(&25), Excluded(&25)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Excluded(&25), Included(&25)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Excluded(&25), Excluded(&25)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );

    assert_eq!(
        s.range((Included(&50), Included(&50)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![50]
    );
    assert_eq!(
        s.range((Included(&50), Excluded(&50)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Excluded(&50), Included(&50)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Excluded(&50), Excluded(&50)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );

    assert_eq!(
        s.range((Included(&100), Included(&-2)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Included(&100), Excluded(&-2)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Excluded(&100), Included(&-2)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
    assert_eq!(
        s.range((Excluded(&100), Excluded(&-2)))
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![]
    );
}

// https://github.com/crossbeam-rs/crossbeam/issues/671
#[test]
fn iter_range2() {
    let set: SkipSet<_> = [1, 3, 5].iter().cloned().collect();
    assert_eq!(set.range(2..4).count(), 1);
    set.insert(3);
}

#[test]
fn into_iter() {
    let s = SkipSet::new();
    for &x in &[4, 2, 12, 8, 7, 11, 5] {
        s.insert(x);
    }

    assert_eq!(s.into_iter().collect::<Vec<_>>(), &[2, 4, 5, 7, 8, 11, 12]);
}

#[test]
fn clear() {
    let s = SkipSet::new();
    for &x in &[4, 2, 12, 8, 7, 11, 5] {
        s.insert(x);
    }

    assert!(!s.is_empty());
    assert_ne!(s.len(), 0);
    s.clear();
    assert!(s.is_empty());
    assert_eq!(s.len(), 0);
}
<|endoftext|>


<commit_before>//! Generates random trees and tree-like plants.

use prop::plant::{Branch, ControlPoint, Tree};
use math::*;
use rand::{Rand, Rng};
use rand::distributions::range::SampleRange;
use rand::distributions::{self, IndependentSample};
use std::ops::Range;
use std::cmp;

/// Parameters for the tree generator.
#[derive(Debug)]
struct Preset {
    name: &'static str,
    /// Diameter of the first branch we create (the trunk).
    trunk_diameter: Range<f32>,
    /// Trunk height. Note that branches going upward can increase plant height
    /// beyond this.
    trunk_height: Range<f32>,
    /// Trunk diameter at `trunk_height`. Should be smaller than
    /// `trunk_diameter`.
    trunk_diameter_top: Range<f32>,
    /// Trunk height at which we start creating branches.
    min_branch_height: Range<f32>,
    /// Range of subbranch diameters as a factor of the parent branch.
    branch_diameter_factor: Range<f32>,
    /// Range of subbranch angles in degrees.
    branch_angle_deg: Range<f32>,
    /// Factor by which to reduce segment diameter between consecutive points,
    /// sampled per branch.
    branch_diam_reduction: Range<f32>,
    /// Range of angles to use for rotation of new segments.
    ///
    /// The higher the angle, the more "twisted" branches appear.
    branch_segment_angle: Range<f32>,
    /// Range of segment counts for branches.
    ///
    /// Together with `branch_segment_length`, this defines the overall branch
    /// length.
    branch_segment_count: Range<u32>,
    /// Range of branch colors for the whole tree, as distinct ranges for R, G
    /// and B.
    branch_color: (Range<f32>, Range<f32>, Range<f32>),
}

static PRESETS: &'static [Preset] = &[Preset {
                                          name: "'Regular' Tree",
                                          trunk_diameter: 0.3..0.5,
                                          trunk_height: 3.0..6.0,
                                          trunk_diameter_top: 0.2..0.4,
                                          min_branch_height: 0.4..0.6,
                                          branch_diameter_factor: 0.3..0.5,
                                          branch_angle_deg: 70.0..110.0,
                                          branch_diam_reduction: 0.75..0.85,
                                          branch_segment_angle: 5.0..15.0,
                                          branch_segment_count: 1..4,
                                          branch_color: (0.3..0.33, 0.1..0.13, 0.0..0.02),
                                      }];

pub struct TreeGen {
    preset: &'static Preset,
    /// Buffer for branches, filled as they're created.
    branches: Vec<Branch>,
}

impl TreeGen {
    /// Generates a new branch at the given start point.
    ///
    /// # Parameters
    ///
    /// * `rng`: The RNG to use
    /// * `start`: The point at which the branch should start
    /// * `dir`: Direction in which the branch should grow
    /// * `depth`: Recursion depth, used to limit the maximum branch depth
    /// * `parent_diam`: Diameter of the node of the parent branch where this
    ///   branch starts
    fn create_branch<R: Rng>(&mut self,
                             rng: &mut R,
                             start: Point3f,
                             dir: Vector3f,
                             depth: u32,
                             parent_diam: f32) {
        if depth > 3 {
            // Limit recursion
            return;
        }

        // Current normalized growing direction, variated slightly as segments are
        // generated
        let mut dir = dir.normalize();

        // Determine starting diameter of the new branch
        let mut diam = range_sample(&self.preset.branch_diameter_factor, rng) * parent_diam;
        // Determine how much segment diameter is reduced
        let diam_factor = range_sample(&self.preset.branch_diam_reduction, rng) * parent_diam;
        // How many segments should this branch get?
        let segment_count = range_sample(&self.preset.branch_segment_count, rng);

        let mut points = vec![ControlPoint {
                                  point: start,
                                  diameter: diam,
                              }];

        {
            let mut last = start;
            // Helper for adding a new point to this branch, which possibly grows a new
            // branch.
            let mut add_point = |dist, diam| {
                // First, get a random angle by which to variate this segment.
                let mut x_angle = range_sample(&self.preset.branch_segment_angle, rng);
                let mut y_angle = range_sample(&self.preset.branch_segment_angle, rng);

                // Invert sign with 50%, to mirror the specified range to the other side
                x_angle = if rng.gen() { -x_angle } else { x_angle };
                y_angle = if rng.gen() { -y_angle } else { y_angle };

                let rotation = Basis3::from(Euler {
                    x: Deg::new(x_angle),
                    y: Deg::new(y_angle),
                    z: Deg::new(0.0),
                });
                dir = rotation.rotate_vector(dir);

                // We need to add a point with a distance of `dist` from `last`.
                let point = last + dir * dist;
                last = point;

                // FIXME Make branch spawn chance configurable
                if rng.gen_weighted_bool(depth * 3) {
                    // Build a vector for the branch direction (Z is up)
                    let dir = self.gen_branch_direction(rng, dir);
                    self.create_branch(rng, point, dir, depth + 1, diam);
                }

                points.push(ControlPoint {
                    point: point,
                    diameter: diam,
                });
            };

            // In a loop, get the length of the next segment from the current diameter.
            for _ in 0..segment_count {
                let length = segment_dist(diam);
                diam *= diam_factor;

                add_point(length, diam);

                if diam < 0.005 {
                    // Bail out at 5mm
                    break;
                }
            }
        }

        assert!(points.len() >= 2,
                "should've generated at least 2 points :(");
        self.branches.push(Branch { points: points });
    }

    /// Given the growing direction of the parent branch, calculates a growing
    /// direction to use for a new child branch.
    fn gen_branch_direction<R: Rng>(&self, rng: &mut R, parent_dir: Vector3f) -> Vector3f {
        // `branch_angle_deg` specifies the angle range in degrees
        let angle = range_sample(&self.preset.branch_angle_deg, rng);

        random_vec_with_angle(rng, parent_dir, angle)
    }

    fn create_trunk<R: Rng>(&mut self, rng: &mut R) {
        let trunk_diameter = range_sample(&self.preset.trunk_diameter, rng);
        let trunk_height = range_sample(&self.preset.trunk_height, rng);
        let trunk_diameter_top = range_sample(&self.preset.trunk_diameter_top, rng);
        let min_branch_height = range_sample(&self.preset.min_branch_height, rng) * trunk_height;

        debug!("trunk diam {} to {}, height {}, branch start at {}",
               trunk_diameter,
               trunk_diameter_top,
               trunk_height,
               min_branch_height);

        let mut points = Vec::new();

        {
            let mut add_point = |height, diam| {
                let point = Point3f::new(0.0, 0.0, height);
                if height >= min_branch_height {
                    // FIXME Make branch spawn chance configurable
                    let branches = &[0, 0, 1, 1, 1, 2];
                    for _ in 0..*rng.choose(branches).unwrap() {
                        // Build a vector for the branch direction (Z is up)
                        let dir = self.gen_branch_direction(rng, Vector3f::new(0.0, 0.0, 1.0));
                        self.create_branch(rng, point, dir, 1, diam);
                    }
                }

                points.push(ControlPoint {
                    point: point,
                    diameter: diam,
                });
            };

            let diam_start = Vector1::new(trunk_diameter);
            let diam_end = Vector1::new(trunk_diameter_top);

            // Split trunk in segments
            // FIXME Vary the segment direction like we do for normal branches
            // FIXME Make segment count depend on the trunk height
            const SEGMENT_COUNT: u32 = 10;
            for i in 0..SEGMENT_COUNT + 1 {
                let height = i as f32 * trunk_height / SEGMENT_COUNT as f32;
                let height_frac = height / trunk_height;
                let diam = diam_start.lerp(diam_end, height_frac);

                add_point(height, diam.x);
            }
        }

        assert!(points.len() >= 2,
                "should've generated at least 2 points :(");
        self.branches.push(Branch { points: points });

        debug!("generated tree with {} branches", self.branches.len());
    }

    /// Generates a random tree according to the stored parameters.
    ///
    /// The tree is returned as a list of branches for now.
    pub fn generate<R: Rng>(mut self, rng: &mut R) -> Tree {
        // Recursively create the tree and put all branches in a buffer.
        self.create_trunk(rng);

        Tree {
            branches: self.branches,
            branch_color: Vector3f::new(range_sample(&self.preset.branch_color.0, rng),
                                        range_sample(&self.preset.branch_color.1, rng),
                                        range_sample(&self.preset.branch_color.2, rng)),
        }
    }
}

impl Rand for TreeGen {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        // Select a random preset that we'll use
        let preset = rng.choose(PRESETS).unwrap().clone();

        TreeGen {
            preset: preset,
            branches: Vec::new(),
        }
    }
}

/// Samples a random element from a range.
fn range_sample<T: SampleRange + cmp::PartialOrd + Copy, R: Rng>(range: &Range<T>,
                                                                 rng: &mut R)
                                                                 -> T {
    // Build a `rand` crate Range. We use `std`s Range for the cool `a..b` syntax ;)
    distributions::Range::new(range.start, range.end).ind_sample(rng)
}

/// Approximation of real-world distance of branch segments, depending on the
/// starting branch diameter.
fn segment_dist(diameter: f32) -> f32 {
    diameter * 11.25
}
<commit_msg>Add more tree presets (treesets?)<commit_after>//! Generates random trees and tree-like plants.

use prop::plant::{Branch, ControlPoint, Tree};
use math::*;
use rand::{Rand, Rng};
use rand::distributions::range::SampleRange;
use rand::distributions::{self, IndependentSample};
use std::ops::Range;
use std::cmp;

/// Parameters for the tree generator.
#[derive(Debug)]
struct Preset {
    name: &'static str,
    /// Diameter of the first branch we create (the trunk).
    trunk_diameter: Range<f32>,
    /// Trunk height. Note that branches going upward can increase plant height
    /// beyond this.
    trunk_height: Range<f32>,
    /// Trunk diameter at `trunk_height`. Should be smaller than
    /// `trunk_diameter`.
    trunk_diameter_top: Range<f32>,
    /// Trunk height at which we start creating branches.
    min_branch_height: Range<f32>,
    /// Range of subbranch diameters as a factor of the parent branch.
    branch_diameter_factor: Range<f32>,
    /// Range of subbranch angles in degrees.
    branch_angle_deg: Range<f32>,
    /// Factor by which to reduce segment diameter between consecutive points,
    /// sampled per branch.
    branch_diam_reduction: Range<f32>,
    /// Range of angles to use for rotation of new segments.
    ///
    /// The higher the angle, the more "twisted" branches appear.
    branch_segment_angle: Range<f32>,
    /// Range of segment counts for branches.
    ///
    /// Together with `branch_segment_length`, this defines the overall branch
    /// length.
    branch_segment_count: Range<u32>,
    /// Range of branch colors for the whole tree, as distinct ranges for R, G
    /// and B.
    branch_color: (Range<f32>, Range<f32>, Range<f32>),
}

static PRESETS: &'static [Preset] = &[Preset {
                                          name: "'Regular' Tree",
                                          trunk_diameter: 0.3..0.5,
                                          trunk_height: 3.0..6.0,
                                          trunk_diameter_top: 0.2..0.4,
                                          min_branch_height: 0.4..0.6,
                                          branch_diameter_factor: 0.3..0.5,
                                          branch_angle_deg: 70.0..110.0,
                                          branch_diam_reduction: 0.75..0.85,
                                          branch_segment_angle: 5.0..15.0,
                                          branch_segment_count: 1..4,
                                          branch_color: (0.3..0.33, 0.1..0.13, 0.0..0.02),
                                      },
                                      Preset {
                                          name: "Shrub",
                                          trunk_diameter: 0.05..0.15,
                                          trunk_height: 0.5..1.5,
                                          trunk_diameter_top: 0.6..0.60001,
                                          min_branch_height: 0.4..0.6,
                                          branch_diameter_factor: 0.3..0.5,
                                          branch_angle_deg: 60.0..100.0,
                                          branch_diam_reduction: 0.70..0.80,
                                          branch_segment_angle: 15.0..20.0,
                                          branch_segment_count: 1..4,
                                          branch_color: (0.15..0.18, 0.03..0.05, 0.0..0.02),
                                      },
                                      Preset {
                                          name: "Cactus",
                                          trunk_diameter: 0.3..0.5,
                                          trunk_height: 2.0..4.0,
                                          trunk_diameter_top: 0.6..0.60001,
                                          min_branch_height: 0.4..0.6,
                                          branch_diameter_factor: 0.3..0.5,
                                          branch_angle_deg: 90.0..90.00001,
                                          branch_diam_reduction: 0.90..0.95,
                                          branch_segment_angle: 0.0..0.00001,
                                          branch_segment_count: 1..2,
                                          branch_color: (0.0..0.05, 0.3..0.33, 0.0..0.02),
                                      }];

pub struct TreeGen {
    preset: &'static Preset,
    /// Buffer for branches, filled as they're created.
    branches: Vec<Branch>,
}

impl TreeGen {
    /// Generates a new branch at the given start point.
    ///
    /// # Parameters
    ///
    /// * `rng`: The RNG to use
    /// * `start`: The point at which the branch should start
    /// * `dir`: Direction in which the branch should grow
    /// * `depth`: Recursion depth, used to limit the maximum branch depth
    /// * `parent_diam`: Diameter of the node of the parent branch where this
    ///   branch starts
    fn create_branch<R: Rng>(&mut self,
                             rng: &mut R,
                             start: Point3f,
                             dir: Vector3f,
                             depth: u32,
                             parent_diam: f32) {
        if depth > 3 {
            // Limit recursion
            return;
        }

        // Current normalized growing direction, variated slightly as segments are
        // generated
        let mut dir = dir.normalize();

        // Determine starting diameter of the new branch
        let mut diam = range_sample(&self.preset.branch_diameter_factor, rng) * parent_diam;
        // Determine how much segment diameter is reduced
        let diam_factor = range_sample(&self.preset.branch_diam_reduction, rng) * parent_diam;
        // How many segments should this branch get?
        let segment_count = range_sample(&self.preset.branch_segment_count, rng);

        let mut points = vec![ControlPoint {
                                  point: start,
                                  diameter: diam,
                              }];

        {
            let mut last = start;
            // Helper for adding a new point to this branch, which possibly grows a new
            // branch.
            let mut add_point = |dist, diam| {
                // First, get a random angle by which to variate this segment.
                let mut x_angle = range_sample(&self.preset.branch_segment_angle, rng);
                let mut y_angle = range_sample(&self.preset.branch_segment_angle, rng);

                // Invert sign with 50%, to mirror the specified range to the other side
                x_angle = if rng.gen() { -x_angle } else { x_angle };
                y_angle = if rng.gen() { -y_angle } else { y_angle };

                let rotation = Basis3::from(Euler {
                    x: Deg::new(x_angle),
                    y: Deg::new(y_angle),
                    z: Deg::new(0.0),
                });
                dir = rotation.rotate_vector(dir);

                // We need to add a point with a distance of `dist` from `last`.
                let point = last + dir * dist;
                last = point;

                // FIXME Make branch spawn chance configurable
                if rng.gen_weighted_bool(depth * 3) {
                    // Build a vector for the branch direction (Z is up)
                    let dir = self.gen_branch_direction(rng, dir);
                    self.create_branch(rng, point, dir, depth + 1, diam);
                }

                points.push(ControlPoint {
                    point: point,
                    diameter: diam,
                });
            };

            // In a loop, get the length of the next segment from the current diameter.
            for _ in 0..segment_count {
                let length = segment_dist(diam);
                diam *= diam_factor;

                add_point(length, diam);

                if diam < 0.005 {
                    // Bail out at 5mm
                    break;
                }
            }
        }

        assert!(points.len() >= 2,
                "should've generated at least 2 points :(");
        self.branches.push(Branch { points: points });
    }

    /// Given the growing direction of the parent branch, calculates a growing
    /// direction to use for a new child branch.
    fn gen_branch_direction<R: Rng>(&self, rng: &mut R, parent_dir: Vector3f) -> Vector3f {
        // `branch_angle_deg` specifies the angle range in degrees
        let angle = range_sample(&self.preset.branch_angle_deg, rng);

        random_vec_with_angle(rng, parent_dir, angle)
    }

    fn create_trunk<R: Rng>(&mut self, rng: &mut R) {
        let trunk_diameter = range_sample(&self.preset.trunk_diameter, rng);
        let trunk_height = range_sample(&self.preset.trunk_height, rng);
        let mut trunk_diameter_top = range_sample(&self.preset.trunk_diameter_top, rng);
        let min_branch_height = range_sample(&self.preset.min_branch_height, rng) * trunk_height;

        // The trunk is supposed to get smaller as we go up, so just enforce that rule here:
        trunk_diameter_top = trunk_diameter.min(trunk_diameter_top);

        debug!("trunk diam {} to {}, height {}, branch start at {}",
               trunk_diameter,
               trunk_diameter_top,
               trunk_height,
               min_branch_height);

        let mut points = Vec::new();

        {
            let mut add_point = |height, diam| {
                let point = Point3f::new(0.0, 0.0, height);
                if height >= min_branch_height {
                    // FIXME Make branch spawn chance configurable
                    let branches = &[0, 0, 1, 1, 1, 2];
                    for _ in 0..*rng.choose(branches).unwrap() {
                        // Build a vector for the branch direction (Z is up)
                        let dir = self.gen_branch_direction(rng, Vector3f::new(0.0, 0.0, 1.0));
                        self.create_branch(rng, point, dir, 1, diam);
                    }
                }

                points.push(ControlPoint {
                    point: point,
                    diameter: diam,
                });
            };

            let diam_start = Vector1::new(trunk_diameter);
            let diam_end = Vector1::new(trunk_diameter_top);

            // Split trunk in segments
            // FIXME Vary the segment direction like we do for normal branches
            // FIXME Make segment count depend on the trunk height
            const SEGMENT_COUNT: u32 = 10;
            for i in 0..SEGMENT_COUNT + 1 {
                let height = i as f32 * trunk_height / SEGMENT_COUNT as f32;
                let height_frac = height / trunk_height;
                let diam = diam_start.lerp(diam_end, height_frac);

                add_point(height, diam.x);
            }
        }

        assert!(points.len() >= 2,
                "should've generated at least 2 points :(");
        self.branches.push(Branch { points: points });

        debug!("generated tree with {} branches", self.branches.len());
    }

    /// Generates a random tree according to the stored parameters.
    ///
    /// The tree is returned as a list of branches for now.
    pub fn generate<R: Rng>(mut self, rng: &mut R) -> Tree {
        // Recursively create the tree and put all branches in a buffer.
        self.create_trunk(rng);

        Tree {
            branches: self.branches,
            branch_color: Vector3f::new(range_sample(&self.preset.branch_color.0, rng),
                                        range_sample(&self.preset.branch_color.1, rng),
                                        range_sample(&self.preset.branch_color.2, rng)),
        }
    }
}

impl Rand for TreeGen {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        // Select a random preset that we'll use
        let preset = rng.choose(PRESETS).unwrap().clone();

        TreeGen {
            preset: preset,
            branches: Vec::new(),
        }
    }
}

/// Samples a random element from a range.
fn range_sample<T: SampleRange + cmp::PartialOrd + Copy, R: Rng>(range: &Range<T>,
                                                                 rng: &mut R)
                                                                 -> T {
    // Build a `rand` crate Range. We use `std`s Range for the cool `a..b` syntax ;)
    distributions::Range::new(range.start, range.end).ind_sample(rng)
}

/// Approximation of real-world distance of branch segments, depending on the
/// starting branch diameter.
fn segment_dist(diameter: f32) -> f32 {
    diameter * 11.25
}
<|endoftext|>


<commit_before>use crate::cpp_code_generator;
use crate::database::CppCheckerAddResult;
use crate::database::CppCheckerEnv;
use crate::database::CppFfiItem;
use crate::database::CppFfiItemKind;
use crate::processor::ProcessingStep;
use crate::processor::ProcessorData;
use log::{debug, info};
use rayon::iter::IndexedParallelIterator;
use rayon::iter::IntoParallelIterator;
use rayon::iter::ParallelIterator;
use rayon::slice::ParallelSliceMut;
use ritual_common::cpp_lib_builder::{
    c2r_cmake_vars, BuildType, CppLibBuilder, CppLibBuilderOutput,
};
use ritual_common::errors::{bail, Result};
use ritual_common::file_utils::{create_dir_all, create_file, path_to_str, remove_dir_all};
use ritual_common::target::current_target;
use ritual_common::utils::MapIfOk;
use ritual_common::utils::ProgressBar;
use std::io::Write;
use std::iter;
use std::path::PathBuf;
use std::time::Instant;

fn check_snippets<'a>(
    data: &mut CppCheckerData,
    snippets: impl Iterator<Item = &'a Snippet>,
) -> Result<CppLibBuilderOutput> {
    {
        let mut file = create_file(&data.main_cpp_path)?;
        writeln!(file, "#include \"utils.h\"")?;
        writeln!(file)?;
        let mut main_content = Vec::new();
        for snippet in snippets {
            match snippet.context {
                SnippetContext::Main => {
                    main_content.push(&snippet.code);
                }
                SnippetContext::Global => {
                    writeln!(file, "{}", snippet.code)?;
                    writeln!(file)?;
                }
            }
        }

        writeln!(file, "int main() {{")?;
        for item in main_content {
            writeln!(file, "{{")?;
            writeln!(file, "{}", item)?;
            writeln!(file, "}}")?;
        }
        writeln!(file, "}}")?;
    }
    info!("env:");
    info!(
        "DYLD_LIBRARY_PATH = {:?}",
        std::env::var("DYLD_LIBRARY_PATH")
    );
    let instant = Instant::now();
    let result = data.builder.run();
    debug!("cpp builder time: {:?}", instant.elapsed());
    result
}

fn snippet_for_item(item: &CppFfiItem) -> Result<Snippet> {
    match &item.kind {
        CppFfiItemKind::Function(cpp_ffi_function) => Ok(Snippet::new_global(
            cpp_code_generator::function_implementation(cpp_ffi_function)?,
        )),
        CppFfiItemKind::QtSlotWrapper(_qt_slot_wrapper) => {
            bail!("qt slot wrappers are not supported yet");
        }
    }
}

struct CppCheckerData {
    main_cpp_path: PathBuf,
    builder: CppLibBuilder,
}

struct CppChecker<'b, 'a: 'b> {
    data: &'b mut ProcessorData<'a>,
}

#[derive(Debug, Clone, Copy)]
enum SnippetContext {
    Main,
    Global,
}

#[derive(Debug, Clone)]
struct Snippet {
    code: String,
    context: SnippetContext,
}

#[derive(Debug, Clone)]
struct SnippetWithIndexes {
    snippet: Snippet,
    ffi_item_index: usize,
    output: Option<CppLibBuilderOutput>,
}

impl Snippet {
    fn new_in_main<S: Into<String>>(code: S) -> Snippet {
        Snippet {
            code: code.into(),
            context: SnippetContext::Main,
        }
    }
    fn new_global<S: Into<String>>(code: S) -> Snippet {
        Snippet {
            code: code.into(),
            context: SnippetContext::Global,
        }
    }
}

struct PreliminaryTest {
    name: String,
    snippet: Snippet,
    expected: bool,
}

impl PreliminaryTest {
    fn new(name: &str, expected: bool, snippet: Snippet) -> Self {
        Self {
            name: name.into(),
            expected,
            snippet,
        }
    }
}

fn binary_check(
    snippets: &mut [SnippetWithIndexes],
    data: &mut CppCheckerData,
    progress_bar: &ProgressBar,
) -> Result<()> {
    if snippets.len() < 3 {
        for snippet in &mut *snippets {
            let output = check_snippets(data, iter::once(&snippet.snippet))?;
            snippet.output = Some(output);
            progress_bar.add(1);
        }
        return Ok(());
    }

    let output = check_snippets(data, snippets.iter().map(|s| &s.snippet))?;
    if let CppLibBuilderOutput::Success = output {
        for snippet in &mut *snippets {
            snippet.output = Some(output.clone());
        }
        progress_bar.add(snippets.len() as u64);
    } else {
        let split_point = snippets.len() / 2;
        let (left, right) = snippets.split_at_mut(split_point);
        binary_check(left, data, progress_bar)?;
        binary_check(right, data, progress_bar)?;
    }
    Ok(())
}

fn check_preliminary_test(data: &mut CppCheckerData, test: &PreliminaryTest) -> Result<()> {
    match check_snippets(data, iter::once(&test.snippet))? {
        CppLibBuilderOutput::Success => {
            if !test.expected {
                bail!("Nevative test ({}) succeeded", test.name);
            }
        }
        CppLibBuilderOutput::Fail(output) => {
            if test.expected {
                bail!("Positive test ({}) failed: {}", test.name, output.stderr);
            }
        }
    }
    Ok(())
}

impl CppChecker<'_, '_> {
    fn env(&self) -> CppCheckerEnv {
        CppCheckerEnv {
            target: current_target(),
            cpp_library_version: self.data.config.cpp_lib_version().map(|s| s.to_string()),
        }
    }
    fn create_instance(&self, id: &str) -> Result<CppCheckerData> {
        let root_path = self.data.workspace.tmp_path().join("cpp_checker").join(id);
        if root_path.exists() {
            remove_dir_all(&root_path)?;
        }
        let src_path = root_path.join("src");
        create_dir_all(&src_path)?;

        let mut cmake_file = create_file(src_path.join("CMakeLists.txt"))?;
        write!(
            cmake_file,
            "{}",
            include_str!("../templates/cpp_checker/CMakeLists.txt")
        )?;

        let mut utils_file = create_file(src_path.join("utils.h"))?;
        write!(
            utils_file,
            include_str!("../templates/cpp_checker/utils.h"),
            include_directives_code = self
                .data
                .config
                .include_directives()
                .map_if_ok(|d| -> Result<_> { Ok(format!("#include \"{}\"", path_to_str(d)?)) })?
                .join("\n")
        )?;

        let mut build_paths = self.data.config.cpp_build_paths().clone();
        build_paths.apply_env();
        let builder = CppLibBuilder {
            cmake_source_dir: src_path.clone(),
            build_dir: root_path.join("build"),
            install_dir: None,
            num_jobs: Some(1),
            build_type: BuildType::Debug,
            cmake_vars: c2r_cmake_vars(
                &self
                    .data
                    .config
                    .cpp_build_config()
                    .eval(&current_target())?,
                &build_paths,
                None,
            )?,
            capture_output: true,
            skip_cmake: false,
            skip_cmake_after_first_run: true,
        };

        Ok(CppCheckerData {
            builder,
            main_cpp_path: src_path.join("main.cpp"),
        })
    }
    fn run(&mut self) -> Result<()> {
        let env = self.env();
        if !self
            .data
            .current_database
            .environments
            .iter()
            .any(|e| e == &env)
        {
            self.data.current_database.environments.push(env.clone());
        }

        let mut snippets = Vec::new();
        for (ffi_item_index, ffi_item) in self.data.current_database.ffi_items.iter().enumerate() {
            if ffi_item.checks.items.iter().any(|check| check.env == env) {
                continue;
            }

            if let Ok(snippet) = snippet_for_item(ffi_item) {
                snippets.push(SnippetWithIndexes {
                    ffi_item_index,
                    snippet,
                    output: None,
                });
            }
        }

        if snippets.is_empty() {
            return Ok(());
        }

        self.run_tests()?;

        let progress_bar = ProgressBar::new(snippets.len() as u64, "Checking items");
        let num_threads = num_cpus::get();
        let div_ceil = |x, y| (x + y - 1) / y;
        let chunk_size = div_ceil(snippets.len(), num_threads);
        let num_chunks = div_ceil(snippets.len(), chunk_size);

        let instances =
            (0..num_chunks).map_if_ok(|index| self.create_instance(&format!("main_{}", index)))?;

        snippets
            .par_chunks_mut(chunk_size)
            .zip_eq(instances.into_par_iter())
            .map(|(chunk, mut instance)| {
                let progress_bar = progress_bar.clone();
                binary_check(chunk, &mut instance, &progress_bar)
            })
            .collect::<Result<_>>()?;

        for snippet in snippets {
            let ffi_item = &mut self.data.current_database.ffi_items[snippet.ffi_item_index];

            let error_data = match snippet.output.unwrap() {
                CppLibBuilderOutput::Success => None, // no error
                CppLibBuilderOutput::Fail(output) => {
                    Some(format!("build failed: {}", output.stderr))
                }
            };

            let r = ffi_item.checks.add(&env, error_data.clone());
            let change_text = match &r {
                CppCheckerAddResult::Added => "Added".to_string(),
                CppCheckerAddResult::Unchanged => "Unchanged".to_string(),
                CppCheckerAddResult::Changed { old } => {
                    format!("Changed! Old data for the same env: {:?}", old)
                }
            };

            debug!(
                "[cpp_checker_update] ffi_item = {:?}; snippet = {:?}; error = {:?}; {}",
                ffi_item, snippet.snippet.code, error_data, change_text
            );
        }
        Ok(())
    }

    fn run_tests(&mut self) -> Result<()> {
        let positive_tests = &[
            PreliminaryTest::new(
                "hello world",
                true,
                Snippet::new_in_main("std::cout << \"Hello world\\n\";"),
            ),
            PreliminaryTest::new(
                "correct assertion",
                true,
                Snippet::new_in_main("ritual_assert(2 + 2 == 4);"),
            ),
            PreliminaryTest::new(
                "type traits",
                true,
                Snippet::new_in_main(
                    "\
                     class C1 {}; \n\
                     enum E1 {};  \n\
                     ritual_assert(std::is_class<C1>::value); \n\
                     ritual_assert(!std::is_class<E1>::value); \n\
                     ritual_assert(!std::is_enum<C1>::value); \n\
                     ritual_assert(std::is_enum<E1>::value); \
                     ritual_assert(sizeof(C1) > 0);\
                     ritual_assert(sizeof(E1) > 0);\n\
                     ",
                ),
            ),
            PreliminaryTest::new(
                "incorrect assertion in fn",
                true,
                Snippet::new_global("int f1() { ritual_assert(2 + 2 == 5); return 1; }"),
            ),
        ];
        assert!(positive_tests.iter().all(|t| t.expected));

        let mut instance = self.create_instance("tests")?;
        let all_positive_output =
            check_snippets(&mut instance, positive_tests.iter().map(|t| &t.snippet))?;
        if all_positive_output != CppLibBuilderOutput::Success {
            for test in positive_tests {
                check_preliminary_test(&mut instance, test)?;
            }
        }

        let negative_tests = &[
            PreliminaryTest::new("syntax error", false, Snippet::new_in_main("}")),
            PreliminaryTest::new(
                "incorrect assertion",
                false,
                Snippet::new_in_main("ritual_assert(2 + 2 == 5);"),
            ),
            PreliminaryTest::new("status code 1", false, Snippet::new_in_main("return 1;")),
        ];
        assert!(negative_tests.iter().all(|t| !t.expected));

        for test in negative_tests {
            check_preliminary_test(&mut instance, test)?;
        }

        Ok(())
    }
}

fn run(data: &mut ProcessorData) -> Result<()> {
    let mut checker = CppChecker { data };

    checker.run()?;

    Ok(())
}

pub fn cpp_checker_step() -> ProcessingStep {
    ProcessingStep::new("cpp_checker", run)
}
<commit_msg>disable debug print<commit_after>use crate::cpp_code_generator;
use crate::database::CppCheckerAddResult;
use crate::database::CppCheckerEnv;
use crate::database::CppFfiItem;
use crate::database::CppFfiItemKind;
use crate::processor::ProcessingStep;
use crate::processor::ProcessorData;
use log::debug;
use rayon::iter::IndexedParallelIterator;
use rayon::iter::IntoParallelIterator;
use rayon::iter::ParallelIterator;
use rayon::slice::ParallelSliceMut;
use ritual_common::cpp_lib_builder::{
    c2r_cmake_vars, BuildType, CppLibBuilder, CppLibBuilderOutput,
};
use ritual_common::errors::{bail, Result};
use ritual_common::file_utils::{create_dir_all, create_file, path_to_str, remove_dir_all};
use ritual_common::target::current_target;
use ritual_common::utils::MapIfOk;
use ritual_common::utils::ProgressBar;
use std::io::Write;
use std::iter;
use std::path::PathBuf;
use std::time::Instant;

fn check_snippets<'a>(
    data: &mut CppCheckerData,
    snippets: impl Iterator<Item = &'a Snippet>,
) -> Result<CppLibBuilderOutput> {
    {
        let mut file = create_file(&data.main_cpp_path)?;
        writeln!(file, "#include \"utils.h\"")?;
        writeln!(file)?;
        let mut main_content = Vec::new();
        for snippet in snippets {
            match snippet.context {
                SnippetContext::Main => {
                    main_content.push(&snippet.code);
                }
                SnippetContext::Global => {
                    writeln!(file, "{}", snippet.code)?;
                    writeln!(file)?;
                }
            }
        }

        writeln!(file, "int main() {{")?;
        for item in main_content {
            writeln!(file, "{{")?;
            writeln!(file, "{}", item)?;
            writeln!(file, "}}")?;
        }
        writeln!(file, "}}")?;
    }
    let instant = Instant::now();
    let result = data.builder.run();
    debug!("cpp builder time: {:?}", instant.elapsed());
    result
}

fn snippet_for_item(item: &CppFfiItem) -> Result<Snippet> {
    match &item.kind {
        CppFfiItemKind::Function(cpp_ffi_function) => Ok(Snippet::new_global(
            cpp_code_generator::function_implementation(cpp_ffi_function)?,
        )),
        CppFfiItemKind::QtSlotWrapper(_qt_slot_wrapper) => {
            bail!("qt slot wrappers are not supported yet");
        }
    }
}

struct CppCheckerData {
    main_cpp_path: PathBuf,
    builder: CppLibBuilder,
}

struct CppChecker<'b, 'a: 'b> {
    data: &'b mut ProcessorData<'a>,
}

#[derive(Debug, Clone, Copy)]
enum SnippetContext {
    Main,
    Global,
}

#[derive(Debug, Clone)]
struct Snippet {
    code: String,
    context: SnippetContext,
}

#[derive(Debug, Clone)]
struct SnippetWithIndexes {
    snippet: Snippet,
    ffi_item_index: usize,
    output: Option<CppLibBuilderOutput>,
}

impl Snippet {
    fn new_in_main<S: Into<String>>(code: S) -> Snippet {
        Snippet {
            code: code.into(),
            context: SnippetContext::Main,
        }
    }
    fn new_global<S: Into<String>>(code: S) -> Snippet {
        Snippet {
            code: code.into(),
            context: SnippetContext::Global,
        }
    }
}

struct PreliminaryTest {
    name: String,
    snippet: Snippet,
    expected: bool,
}

impl PreliminaryTest {
    fn new(name: &str, expected: bool, snippet: Snippet) -> Self {
        Self {
            name: name.into(),
            expected,
            snippet,
        }
    }
}

fn binary_check(
    snippets: &mut [SnippetWithIndexes],
    data: &mut CppCheckerData,
    progress_bar: &ProgressBar,
) -> Result<()> {
    if snippets.len() < 3 {
        for snippet in &mut *snippets {
            let output = check_snippets(data, iter::once(&snippet.snippet))?;
            snippet.output = Some(output);
            progress_bar.add(1);
        }
        return Ok(());
    }

    let output = check_snippets(data, snippets.iter().map(|s| &s.snippet))?;
    if let CppLibBuilderOutput::Success = output {
        for snippet in &mut *snippets {
            snippet.output = Some(output.clone());
        }
        progress_bar.add(snippets.len() as u64);
    } else {
        let split_point = snippets.len() / 2;
        let (left, right) = snippets.split_at_mut(split_point);
        binary_check(left, data, progress_bar)?;
        binary_check(right, data, progress_bar)?;
    }
    Ok(())
}

fn check_preliminary_test(data: &mut CppCheckerData, test: &PreliminaryTest) -> Result<()> {
    match check_snippets(data, iter::once(&test.snippet))? {
        CppLibBuilderOutput::Success => {
            if !test.expected {
                bail!("Nevative test ({}) succeeded", test.name);
            }
        }
        CppLibBuilderOutput::Fail(output) => {
            if test.expected {
                bail!("Positive test ({}) failed: {}", test.name, output.stderr);
            }
        }
    }
    Ok(())
}

impl CppChecker<'_, '_> {
    fn env(&self) -> CppCheckerEnv {
        CppCheckerEnv {
            target: current_target(),
            cpp_library_version: self.data.config.cpp_lib_version().map(|s| s.to_string()),
        }
    }
    fn create_instance(&self, id: &str) -> Result<CppCheckerData> {
        let root_path = self.data.workspace.tmp_path().join("cpp_checker").join(id);
        if root_path.exists() {
            remove_dir_all(&root_path)?;
        }
        let src_path = root_path.join("src");
        create_dir_all(&src_path)?;

        let mut cmake_file = create_file(src_path.join("CMakeLists.txt"))?;
        write!(
            cmake_file,
            "{}",
            include_str!("../templates/cpp_checker/CMakeLists.txt")
        )?;

        let mut utils_file = create_file(src_path.join("utils.h"))?;
        write!(
            utils_file,
            include_str!("../templates/cpp_checker/utils.h"),
            include_directives_code = self
                .data
                .config
                .include_directives()
                .map_if_ok(|d| -> Result<_> { Ok(format!("#include \"{}\"", path_to_str(d)?)) })?
                .join("\n")
        )?;

        let mut build_paths = self.data.config.cpp_build_paths().clone();
        build_paths.apply_env();
        let builder = CppLibBuilder {
            cmake_source_dir: src_path.clone(),
            build_dir: root_path.join("build"),
            install_dir: None,
            num_jobs: Some(1),
            build_type: BuildType::Debug,
            cmake_vars: c2r_cmake_vars(
                &self
                    .data
                    .config
                    .cpp_build_config()
                    .eval(&current_target())?,
                &build_paths,
                None,
            )?,
            capture_output: true,
            skip_cmake: false,
            skip_cmake_after_first_run: true,
        };

        Ok(CppCheckerData {
            builder,
            main_cpp_path: src_path.join("main.cpp"),
        })
    }
    fn run(&mut self) -> Result<()> {
        let env = self.env();
        if !self
            .data
            .current_database
            .environments
            .iter()
            .any(|e| e == &env)
        {
            self.data.current_database.environments.push(env.clone());
        }

        let mut snippets = Vec::new();
        for (ffi_item_index, ffi_item) in self.data.current_database.ffi_items.iter().enumerate() {
            if ffi_item.checks.items.iter().any(|check| check.env == env) {
                continue;
            }

            if let Ok(snippet) = snippet_for_item(ffi_item) {
                snippets.push(SnippetWithIndexes {
                    ffi_item_index,
                    snippet,
                    output: None,
                });
            }
        }

        if snippets.is_empty() {
            return Ok(());
        }

        self.run_tests()?;

        let progress_bar = ProgressBar::new(snippets.len() as u64, "Checking items");
        let num_threads = num_cpus::get();
        let div_ceil = |x, y| (x + y - 1) / y;
        let chunk_size = div_ceil(snippets.len(), num_threads);
        let num_chunks = div_ceil(snippets.len(), chunk_size);

        let instances =
            (0..num_chunks).map_if_ok(|index| self.create_instance(&format!("main_{}", index)))?;

        snippets
            .par_chunks_mut(chunk_size)
            .zip_eq(instances.into_par_iter())
            .map(|(chunk, mut instance)| {
                let progress_bar = progress_bar.clone();
                binary_check(chunk, &mut instance, &progress_bar)
            })
            .collect::<Result<_>>()?;

        for snippet in snippets {
            let ffi_item = &mut self.data.current_database.ffi_items[snippet.ffi_item_index];

            let error_data = match snippet.output.unwrap() {
                CppLibBuilderOutput::Success => None, // no error
                CppLibBuilderOutput::Fail(output) => {
                    Some(format!("build failed: {}", output.stderr))
                }
            };

            let r = ffi_item.checks.add(&env, error_data.clone());
            let change_text = match &r {
                CppCheckerAddResult::Added => "Added".to_string(),
                CppCheckerAddResult::Unchanged => "Unchanged".to_string(),
                CppCheckerAddResult::Changed { old } => {
                    format!("Changed! Old data for the same env: {:?}", old)
                }
            };

            debug!(
                "[cpp_checker_update] ffi_item = {:?}; snippet = {:?}; error = {:?}; {}",
                ffi_item, snippet.snippet.code, error_data, change_text
            );
        }
        Ok(())
    }

    fn run_tests(&mut self) -> Result<()> {
        let positive_tests = &[
            PreliminaryTest::new(
                "hello world",
                true,
                Snippet::new_in_main("std::cout << \"Hello world\\n\";"),
            ),
            PreliminaryTest::new(
                "correct assertion",
                true,
                Snippet::new_in_main("ritual_assert(2 + 2 == 4);"),
            ),
            PreliminaryTest::new(
                "type traits",
                true,
                Snippet::new_in_main(
                    "\
                     class C1 {}; \n\
                     enum E1 {};  \n\
                     ritual_assert(std::is_class<C1>::value); \n\
                     ritual_assert(!std::is_class<E1>::value); \n\
                     ritual_assert(!std::is_enum<C1>::value); \n\
                     ritual_assert(std::is_enum<E1>::value); \
                     ritual_assert(sizeof(C1) > 0);\
                     ritual_assert(sizeof(E1) > 0);\n\
                     ",
                ),
            ),
            PreliminaryTest::new(
                "incorrect assertion in fn",
                true,
                Snippet::new_global("int f1() { ritual_assert(2 + 2 == 5); return 1; }"),
            ),
        ];
        assert!(positive_tests.iter().all(|t| t.expected));

        let mut instance = self.create_instance("tests")?;
        let all_positive_output =
            check_snippets(&mut instance, positive_tests.iter().map(|t| &t.snippet))?;
        if all_positive_output != CppLibBuilderOutput::Success {
            for test in positive_tests {
                check_preliminary_test(&mut instance, test)?;
            }
        }

        let negative_tests = &[
            PreliminaryTest::new("syntax error", false, Snippet::new_in_main("}")),
            PreliminaryTest::new(
                "incorrect assertion",
                false,
                Snippet::new_in_main("ritual_assert(2 + 2 == 5);"),
            ),
            PreliminaryTest::new("status code 1", false, Snippet::new_in_main("return 1;")),
        ];
        assert!(negative_tests.iter().all(|t| !t.expected));

        for test in negative_tests {
            check_preliminary_test(&mut instance, test)?;
        }

        Ok(())
    }
}

fn run(data: &mut ProcessorData) -> Result<()> {
    let mut checker = CppChecker { data };

    checker.run()?;

    Ok(())
}

pub fn cpp_checker_step() -> ProcessingStep {
    ProcessingStep::new("cpp_checker", run)
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate test;

extern crate crossbeam;
extern crate rayon;
extern crate parry;
use parry::{Expression, Value};

use test::Bencher as B;

fn add_seq(b: &mut B, n: i64) {
    let x = (0..n).collect::<Vec<_>>();
    let y = x.clone();
    let mut out = x.clone();
    b.iter(|| {
        let values = parry::iterators::Binary::new(parry::iterators::Plus,
                                                   x.iter().cloned(), y.iter().cloned())
            .map(|x| 1_000_000 / (x + 1));
        for (o, i) in out.iter_mut().zip(values) {
            *o = i;
        }
    })
}
fn add_par(b: &mut B, n: i64) {
    rayon::initialize();
    let x = (0..n).collect::<Vec<_>>();
    let y = x.clone();
    let mut out = x.clone();
    b.iter(|| {
        let e = Value(&test::black_box(&x)[..]) + Value(&test::black_box(&y)[..]);
        let f = |x| 1_000_000 / (x + 1);
        parry::evaluate(&mut out, e.map(&f));
    })
}

macro_rules! tests {
    ($par: ident, $seq: ident: $($par_name: ident, $seq_name: ident;)*) => {
        $(
            #[bench]
            fn $par_name(b: &mut B) {
                let n = stringify!($par_name).split('_').nth(1).unwrap().parse().unwrap();
                add_par(b, n)
            }
            #[bench]
            fn $seq_name(b: &mut B) {
                let n = stringify!($par_name).split('_').nth(1).unwrap().parse().unwrap();
                add_seq(b, n)
            }
            )*
    }
}

tests! {
    add_par, add_seq:
    add_0001000_p, add_0001000_s;
    add_0010000_p, add_0010000_s;
    add_0100000_p, add_0100000_s;
    add_1000000_p, add_1000000_s;
}
<commit_msg>Use constants to make the benchmark cleaner.<commit_after>#![feature(test)]
extern crate test;

extern crate crossbeam;
extern crate rayon;
extern crate parry;
use parry::{Expression, Constant, Value};

use test::Bencher as B;

fn add_seq(b: &mut B, n: i64) {
    let x = (0..n).collect::<Vec<_>>();
    let y = x.clone();
    let mut out = x.clone();
    b.iter(|| {
        let values = parry::iterators::Binary::new(parry::iterators::Plus,
                                                   x.iter().cloned(), y.iter().cloned())
            .map(|x| 1_000_000 / (x + 1));
        for (o, i) in out.iter_mut().zip(values) {
            *o = i;
        }
    })
}
fn add_par(b: &mut B, n: i64) {
    rayon::initialize();
    let x = (0..n).collect::<Vec<_>>();
    let y = x.clone();
    let mut out = x.clone();
    b.iter(|| {
        let e = Constant(1_000_000) / (Value(&test::black_box(&x)[..]) + &test::black_box(&y)[..]
                                       + Constant(1));
        parry::evaluate(&mut out, e);
    })
}

macro_rules! tests {
    ($par: ident, $seq: ident: $($par_name: ident, $seq_name: ident;)*) => {
        $(
            #[bench]
            fn $par_name(b: &mut B) {
                let n = stringify!($par_name).split('_').nth(1).unwrap().parse().unwrap();
                add_par(b, n)
            }
            #[bench]
            fn $seq_name(b: &mut B) {
                let n = stringify!($par_name).split('_').nth(1).unwrap().parse().unwrap();
                add_seq(b, n)
            }
            )*
    }
}

tests! {
    add_par, add_seq:
    add_0001000_p, add_0001000_s;
    add_0010000_p, add_0010000_s;
    add_0100000_p, add_0100000_s;
    add_1000000_p, add_1000000_s;
}
<|endoftext|>