<commit_before>use super::*;

use dpi::LogicalPosition;
use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, MouseScrollDelta, StartCause, TouchPhase, WindowEvent};
use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed};
use instant::{Duration, Instant};
use window::{WindowId as RootWI};
use stdweb::{
    traits::*,
    web::{
        document,
        event::*,
        html_element::CanvasElement,
        window,
        TimeoutHandle,
    },
};
use std::{
    cell::RefCell,
    collections::{VecDeque, vec_deque::IntoIter as VecDequeIter},
    clone::Clone,
    marker::PhantomData,
    rc::Rc,
};

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(i32);

impl DeviceId {
    pub unsafe fn dummy() -> Self {
        DeviceId(0)
    }
}

pub struct EventLoop<T: 'static> {
    elw: RootELW<T>,
}

pub struct EventLoopWindowTarget<T: 'static> {
    pub(crate) runner: EventLoopRunnerShared<T>,
}

impl<T> EventLoopWindowTarget<T> {
    fn new() -> Self {
        EventLoopWindowTarget {
            runner: EventLoopRunnerShared(Rc::new(ELRShared {
                runner: RefCell::new(None),
                events: RefCell::new(VecDeque::new())
            }))
        }
    }
}

#[derive(Clone)]
pub struct EventLoopProxy<T: 'static> {
    runner: EventLoopRunnerShared<T>
}

impl<T: 'static> EventLoopProxy<T> {
    pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
        self.runner.send_event(Event::UserEvent(event));
        Ok(())
    }
}

pub struct EventLoopRunnerShared<T>(Rc<ELRShared<T>>);

impl<T> Clone for EventLoopRunnerShared<T> {
    fn clone(&self) -> Self {
        EventLoopRunnerShared(self.0.clone())
    }
}

pub struct ELRShared<T> {
    runner: RefCell<Option<EventLoopRunner<T>>>,
    events: RefCell<VecDeque<Event<T>>>,
}

struct EventLoopRunner<T> {
    control: ControlFlowStatus,
    is_busy: bool,
    event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
}

enum ControlFlowStatus {
    Init,
    WaitUntil {
        timeout: TimeoutHandle,
        start: Instant,
        end: Instant
    },
    Wait {
        start: Instant,
    },
    Poll {
        timeout: TimeoutHandle
    },
    Exit
}

impl ControlFlowStatus {
    fn to_control_flow(&self) -> ControlFlow {
        match self {
            ControlFlowStatus::Init => ControlFlow::Poll, // During the Init loop, the user should get Poll, the default control value
            ControlFlowStatus::WaitUntil { end, .. } => ControlFlow::WaitUntil(*end),
            ControlFlowStatus::Wait { .. } => ControlFlow::Wait,
            ControlFlowStatus::Poll { .. } => ControlFlow::Poll,
            ControlFlowStatus::Exit => ControlFlow::Exit,
        }
    }

    fn is_exit(&self) -> bool {
        match self {
            ControlFlowStatus::Exit => true,
            _ => false,
        }
    }
}

impl<T> EventLoop<T> {
    pub fn new() -> Self {
        EventLoop {
            elw: RootELW {
                p: EventLoopWindowTarget::new(),
                _marker: PhantomData
            },
        }
    }

    pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {
        VecDeque::new().into_iter()
    }

    pub fn primary_monitor(&self) -> MonitorHandle {
        MonitorHandle
    }

    pub fn run<F>(self, mut event_handler: F) -> !
        where F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow) {
        let runner = self.elw.p.runner;

        let relw = RootELW {
            p: EventLoopWindowTarget::new(),
            _marker: PhantomData
        };
        runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl)));

        let document = &document();
        add_event(&runner, document, |elrs, _: BlurEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::Focused(false)
            });
        });
        add_event(&runner, document, |elrs, _: FocusEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::Focused(true)
            });

        });
        add_event(&runner, document, |elrs, event: KeyDownEvent| {
            let key = event.key();
            let mut characters = key.chars();
            let first = characters.next();
            let second = characters.next();
            if let (Some(key), None) = (first, second) {
                elrs.send_event(Event::WindowEvent {
                    window_id: RootWI(WindowId),
                    event: WindowEvent::ReceivedCharacter(key)
                });
            }
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::KeyboardInput {
                    device_id: RootDI(unsafe { DeviceId::dummy() }),
                    input: KeyboardInput {
                        scancode: scancode(&event),
                        state: ElementState::Pressed,
                        virtual_keycode: button_mapping(&event),
                        modifiers: keyboard_modifiers_state(&event),
                    }
                }
            });
        });
        add_event(&runner, document, |elrs, event: KeyUpEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::KeyboardInput {
                    device_id: RootDI(unsafe { DeviceId::dummy() }),
                    input: KeyboardInput {
                        scancode: scancode(&event),
                        state: ElementState::Released,
                        virtual_keycode: button_mapping(&event),
                        modifiers: keyboard_modifiers_state(&event),
                    }
                }
            });
        });

        stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here?

        // Throw an exception to break out of Rust exceution and use unreachable to tell the
        // compiler this function won't return, giving it a return type of '!'
        js! {
            throw "Using exceptions for control flow, don't mind me. This isn't actually an error!";
        }
        unreachable!();
    }

    pub fn create_proxy(&self) -> EventLoopProxy<T> {
        EventLoopProxy {
            runner: self.elw.p.runner.clone()
        }
    }

    pub fn window_target(&self) -> &RootELW<T> {
        &self.elw
    }
}

pub fn register<T: 'static>(elrs: &EventLoopRunnerShared<T>, canvas: &CanvasElement) {
    add_event(elrs, canvas, |elrs, event: PointerOutEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorLeft {
                device_id: RootDI(DeviceId(event.pointer_id()))
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerOverEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorEntered {
                device_id: RootDI(DeviceId(event.pointer_id()))
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerMoveEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorMoved {
                device_id: RootDI(DeviceId(event.pointer_id())),
                position: LogicalPosition {
                    x: event.offset_x(),
                    y: event.offset_y()
                },
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerUpEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseInput {
                device_id: RootDI(DeviceId(event.pointer_id())),
                state: ElementState::Pressed,
                button: mouse_button(&event),
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerDownEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseInput {
                device_id: RootDI(DeviceId(event.pointer_id())),
                state: ElementState::Released,
                button: mouse_button(&event),
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: MouseWheelEvent| {
        let x = event.delta_x();
        let y = event.delta_y();
        let delta = match event.delta_mode() {
            MouseWheelDeltaMode::Line => MouseScrollDelta::LineDelta(x as f32, y as f32),
            MouseWheelDeltaMode::Pixel => MouseScrollDelta::PixelDelta(LogicalPosition { x, y }),
            MouseWheelDeltaMode::Page => return,
        };
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseWheel {
                device_id: RootDI(DeviceId(0)),
                delta,
                phase: TouchPhase::Moved,
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
}

fn add_event<T: 'static, E, F>(elrs: &EventLoopRunnerShared<T>, target: &impl IEventTarget, mut handler: F)
        where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared<T>, E) + 'static {
    let elrs = elrs.clone();

    target.add_event_listener(move |event: E| {
        // Don't capture the event if the events loop has been destroyed
        match &*elrs.0.runner.borrow() {
            Some(ref runner) if runner.control.is_exit() => return,
            _ => ()
        }

        event.prevent_default();
        event.stop_propagation();
        event.cancel_bubble();

        handler(&elrs, event);
    });
}

impl<T: 'static> EventLoopRunnerShared<T> {
    // Set the event callback to use for the event loop runner
    // This the event callback is a fairly thin layer over the user-provided callback that closes
    // over a RootEventLoopWindowTarget reference
    fn set_listener(&self, event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>) {
        *self.0.runner.borrow_mut() = Some(EventLoopRunner {
            control: ControlFlowStatus::Init,
            is_busy: false,
            event_handler,
        });
        self.send_event(Event::NewEvents(StartCause::Init));
    }

    // Add an event to the event loop runner
    //
    // It will determine if the event should be immediately sent to the user or buffered for later
    pub fn send_event(&self, event: Event<T>) {
        // If the event loop is closed, it should discard any new events
        if self.closed() {
            return;
        }

        // Determine if event handling is in process, and then release the borrow on the runner
        let (start_cause, event_is_start) = match *self.0.runner.borrow() {
            Some(ref runner) if !runner.is_busy => {
                if let Event::NewEvents(cause) = event {
                    (cause, true)
                } else {
                    (match runner.control {
                        ControlFlowStatus::Init => StartCause::Init,
                        ControlFlowStatus::Poll { ref timeout } =>  {
                            timeout.clear();

                            StartCause::Poll
                        }
                        ControlFlowStatus::Wait { start } => StartCause::WaitCancelled {
                            start,
                            requested_resume: None,
                        },
                        ControlFlowStatus::WaitUntil { start, end, ref timeout } => {
                            timeout.clear();

                            StartCause::WaitCancelled {
                                start,
                                requested_resume: Some(end)
                            }
                        },
                        ControlFlowStatus::Exit => { return; }
                    }, false)
                }
            }
            _ => {
                // Events are currently being handled, so queue this one and don't try to
                // double-process the event queue
                self.0.events.borrow_mut().push_back(event);
                return;
            }
        };
        let mut control = self.current_control_flow();
        // Handle starting a new batch of events
        //
        // The user is informed via Event::NewEvents that there is a batch of events to process
        // However, there is only one of these per batch of events
        self.handle_event(Event::NewEvents(start_cause), &mut control);
        if !event_is_start {
            self.handle_event(event, &mut control);
        }
        self.handle_event(Event::EventsCleared, &mut control);
        self.apply_control_flow(control);
        // If the event loop is closed, it has been closed this iteration and now the closing
        // event should be emitted
        if self.closed() {
            self.handle_event(Event::LoopDestroyed, &mut control);
        }
    }

    // handle_event takes in events and either queues them or applies a callback
    //
    // It should only ever be called from send_event
    fn handle_event(&self, event: Event<T>, control: &mut ControlFlow) {
        let closed = self.closed();

        match *self.0.runner.borrow_mut() {
            Some(ref mut runner) => {
                // An event is being processed, so the runner should be marked busy
                runner.is_busy = true;

                (runner.event_handler)(event, control);

                // Maintain closed state, even if the callback changes it
                if closed {
                    *control = ControlFlow::Exit;
                }

                // An event is no longer being processed
                runner.is_busy = false;
            }
            // If an event is being handled without a runner somehow, add it to the event queue so
            // it will eventually be processed
            _ => self.0.events.borrow_mut().push_back(event)
        }

        // Don't take events out of the queue if the loop is closed or the runner doesn't exist
        // If the runner doesn't exist and this method recurses, it will recurse infinitely
        if !closed && self.0.runner.borrow().is_some() {
            // Take an event out of the queue and handle it
            if let Some(event) = self.0.events.borrow_mut().pop_front() {
                self.handle_event(event, control);
            }
        }
    }

    // Apply the new ControlFlow that has been selected by the user
    // Start any necessary timeouts etc
    fn apply_control_flow(&self, control_flow: ControlFlow) {
        let control_flow_status = match control_flow {
           ControlFlow::Poll => {
                let cloned = self.clone();
                ControlFlowStatus::Poll {
                    timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), 0)
                }
            }
            ControlFlow::Wait => ControlFlowStatus::Wait { start: Instant::now() },
            ControlFlow::WaitUntil(end) => {
                let cloned = self.clone();
                let start = Instant::now();
                let delay = if end <= start {
                    Duration::from_millis(0)
                } else {
                    end - start
                };
                ControlFlowStatus::WaitUntil {
                    start,
                    end,
                    timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), delay.as_millis() as u32)
                }
            }
            ControlFlow::Exit => ControlFlowStatus::Exit,
        };

        match *self.0.runner.borrow_mut() {
            Some(ref mut runner) => {
                runner.control = control_flow_status;
            }
            None => ()
        }
    }

    // Check if the event loop is currntly closed
    fn closed(&self) -> bool {
        match *self.0.runner.borrow() {
            Some(ref runner) => runner.control.is_exit(),
            None => false, // If the event loop is None, it has not been intialised yet, so it cannot be closed
        }
    }

    // Get the current control flow state
    fn current_control_flow(&self) -> ControlFlow {
        match *self.0.runner.borrow() {
            Some(ref runner) => runner.control.to_control_flow(),
            None => ControlFlow::Poll,
        }
    }
}

<commit_msg>Update how timeouts are cleared to avoid possible double-clearing<commit_after>use super::*;

use dpi::LogicalPosition;
use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, MouseScrollDelta, StartCause, TouchPhase, WindowEvent};
use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed};
use instant::{Duration, Instant};
use window::{WindowId as RootWI};
use stdweb::{
    traits::*,
    web::{
        document,
        event::*,
        html_element::CanvasElement,
        window,
        TimeoutHandle,
    },
};
use std::{
    cell::RefCell,
    collections::{VecDeque, vec_deque::IntoIter as VecDequeIter},
    clone::Clone,
    marker::PhantomData,
    rc::Rc,
};

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(i32);

impl DeviceId {
    pub unsafe fn dummy() -> Self {
        DeviceId(0)
    }
}

pub struct EventLoop<T: 'static> {
    elw: RootELW<T>,
}

pub struct EventLoopWindowTarget<T: 'static> {
    pub(crate) runner: EventLoopRunnerShared<T>,
}

impl<T> EventLoopWindowTarget<T> {
    fn new() -> Self {
        EventLoopWindowTarget {
            runner: EventLoopRunnerShared(Rc::new(ELRShared {
                runner: RefCell::new(None),
                events: RefCell::new(VecDeque::new())
            }))
        }
    }
}

#[derive(Clone)]
pub struct EventLoopProxy<T: 'static> {
    runner: EventLoopRunnerShared<T>
}

impl<T: 'static> EventLoopProxy<T> {
    pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
        self.runner.send_event(Event::UserEvent(event));
        Ok(())
    }
}

pub struct EventLoopRunnerShared<T>(Rc<ELRShared<T>>);

impl<T> Clone for EventLoopRunnerShared<T> {
    fn clone(&self) -> Self {
        EventLoopRunnerShared(self.0.clone())
    }
}

pub struct ELRShared<T> {
    runner: RefCell<Option<EventLoopRunner<T>>>,
    events: RefCell<VecDeque<Event<T>>>,
}

struct EventLoopRunner<T> {
    control: ControlFlowStatus,
    is_busy: bool,
    event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
}

enum ControlFlowStatus {
    Init,
    WaitUntil {
        timeout: TimeoutHandle,
        start: Instant,
        end: Instant
    },
    Wait {
        start: Instant,
    },
    Poll {
        timeout: TimeoutHandle
    },
    Exit
}

impl ControlFlowStatus {
    fn to_control_flow(&self) -> ControlFlow {
        match self {
            ControlFlowStatus::Init => ControlFlow::Poll, // During the Init loop, the user should get Poll, the default control value
            ControlFlowStatus::WaitUntil { end, .. } => ControlFlow::WaitUntil(*end),
            ControlFlowStatus::Wait { .. } => ControlFlow::Wait,
            ControlFlowStatus::Poll { .. } => ControlFlow::Poll,
            ControlFlowStatus::Exit => ControlFlow::Exit,
        }
    }

    fn is_exit(&self) -> bool {
        match self {
            ControlFlowStatus::Exit => true,
            _ => false,
        }
    }
}

impl<T> EventLoop<T> {
    pub fn new() -> Self {
        EventLoop {
            elw: RootELW {
                p: EventLoopWindowTarget::new(),
                _marker: PhantomData
            },
        }
    }

    pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {
        VecDeque::new().into_iter()
    }

    pub fn primary_monitor(&self) -> MonitorHandle {
        MonitorHandle
    }

    pub fn run<F>(self, mut event_handler: F) -> !
        where F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow) {
        let runner = self.elw.p.runner;

        let relw = RootELW {
            p: EventLoopWindowTarget::new(),
            _marker: PhantomData
        };
        runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl)));

        let document = &document();
        add_event(&runner, document, |elrs, _: BlurEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::Focused(false)
            });
        });
        add_event(&runner, document, |elrs, _: FocusEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::Focused(true)
            });

        });
        add_event(&runner, document, |elrs, event: KeyDownEvent| {
            let key = event.key();
            let mut characters = key.chars();
            let first = characters.next();
            let second = characters.next();
            if let (Some(key), None) = (first, second) {
                elrs.send_event(Event::WindowEvent {
                    window_id: RootWI(WindowId),
                    event: WindowEvent::ReceivedCharacter(key)
                });
            }
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::KeyboardInput {
                    device_id: RootDI(unsafe { DeviceId::dummy() }),
                    input: KeyboardInput {
                        scancode: scancode(&event),
                        state: ElementState::Pressed,
                        virtual_keycode: button_mapping(&event),
                        modifiers: keyboard_modifiers_state(&event),
                    }
                }
            });
        });
        add_event(&runner, document, |elrs, event: KeyUpEvent| {
            elrs.send_event(Event::WindowEvent {
                window_id: RootWI(WindowId),
                event: WindowEvent::KeyboardInput {
                    device_id: RootDI(unsafe { DeviceId::dummy() }),
                    input: KeyboardInput {
                        scancode: scancode(&event),
                        state: ElementState::Released,
                        virtual_keycode: button_mapping(&event),
                        modifiers: keyboard_modifiers_state(&event),
                    }
                }
            });
        });

        stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here?

        // Throw an exception to break out of Rust exceution and use unreachable to tell the
        // compiler this function won't return, giving it a return type of '!'
        js! {
            throw "Using exceptions for control flow, don't mind me. This isn't actually an error!";
        }
        unreachable!();
    }

    pub fn create_proxy(&self) -> EventLoopProxy<T> {
        EventLoopProxy {
            runner: self.elw.p.runner.clone()
        }
    }

    pub fn window_target(&self) -> &RootELW<T> {
        &self.elw
    }
}

pub fn register<T: 'static>(elrs: &EventLoopRunnerShared<T>, canvas: &CanvasElement) {
    add_event(elrs, canvas, |elrs, event: PointerOutEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorLeft {
                device_id: RootDI(DeviceId(event.pointer_id()))
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerOverEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorEntered {
                device_id: RootDI(DeviceId(event.pointer_id()))
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerMoveEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::CursorMoved {
                device_id: RootDI(DeviceId(event.pointer_id())),
                position: LogicalPosition {
                    x: event.offset_x(),
                    y: event.offset_y()
                },
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerUpEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseInput {
                device_id: RootDI(DeviceId(event.pointer_id())),
                state: ElementState::Pressed,
                button: mouse_button(&event),
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: PointerDownEvent| {
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseInput {
                device_id: RootDI(DeviceId(event.pointer_id())),
                state: ElementState::Released,
                button: mouse_button(&event),
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
    add_event(elrs, canvas, |elrs, event: MouseWheelEvent| {
        let x = event.delta_x();
        let y = event.delta_y();
        let delta = match event.delta_mode() {
            MouseWheelDeltaMode::Line => MouseScrollDelta::LineDelta(x as f32, y as f32),
            MouseWheelDeltaMode::Pixel => MouseScrollDelta::PixelDelta(LogicalPosition { x, y }),
            MouseWheelDeltaMode::Page => return,
        };
        elrs.send_event(Event::WindowEvent {
            window_id: RootWI(WindowId),
            event: WindowEvent::MouseWheel {
                device_id: RootDI(DeviceId(0)),
                delta,
                phase: TouchPhase::Moved,
                modifiers: mouse_modifiers_state(&event)
            }
        });
    });
}

fn add_event<T: 'static, E, F>(elrs: &EventLoopRunnerShared<T>, target: &impl IEventTarget, mut handler: F)
        where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared<T>, E) + 'static {
    let elrs = elrs.clone();

    target.add_event_listener(move |event: E| {
        // Don't capture the event if the events loop has been destroyed
        match &*elrs.0.runner.borrow() {
            Some(ref runner) if runner.control.is_exit() => return,
            _ => ()
        }

        event.prevent_default();
        event.stop_propagation();
        event.cancel_bubble();

        handler(&elrs, event);
    });
}

impl<T: 'static> EventLoopRunnerShared<T> {
    // Set the event callback to use for the event loop runner
    // This the event callback is a fairly thin layer over the user-provided callback that closes
    // over a RootEventLoopWindowTarget reference
    fn set_listener(&self, event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>) {
        *self.0.runner.borrow_mut() = Some(EventLoopRunner {
            control: ControlFlowStatus::Init,
            is_busy: false,
            event_handler,
        });
        self.send_event(Event::NewEvents(StartCause::Init));
    }

    // Add an event to the event loop runner
    //
    // It will determine if the event should be immediately sent to the user or buffered for later
    pub fn send_event(&self, event: Event<T>) {
        // If the event loop is closed, it should discard any new events
        if self.closed() {
            return;
        }

        // Determine if event handling is in process, and then release the borrow on the runner
        let (start_cause, event_is_start) = match *self.0.runner.borrow() {
            Some(ref runner) if !runner.is_busy => {
                if let Event::NewEvents(cause) = event {
                    (cause, true)
                } else {
                    (match runner.control {
                        ControlFlowStatus::Init => StartCause::Init,
                        ControlFlowStatus::Poll { .. } =>  {
                            StartCause::Poll
                        }
                        ControlFlowStatus::Wait { start } => StartCause::WaitCancelled {
                            start,
                            requested_resume: None,
                        },
                        ControlFlowStatus::WaitUntil { start, end, .. } => {
                            StartCause::WaitCancelled {
                                start,
                                requested_resume: Some(end)
                            }
                        },
                        ControlFlowStatus::Exit => { return; }
                    }, false)
                }
            }
            _ => {
                // Events are currently being handled, so queue this one and don't try to
                // double-process the event queue
                self.0.events.borrow_mut().push_back(event);
                return;
            }
        };
        let mut control = self.current_control_flow();
        // Handle starting a new batch of events
        //
        // The user is informed via Event::NewEvents that there is a batch of events to process
        // However, there is only one of these per batch of events
        self.handle_event(Event::NewEvents(start_cause), &mut control);
        if !event_is_start {
            self.handle_event(event, &mut control);
        }
        self.handle_event(Event::EventsCleared, &mut control);
        self.apply_control_flow(control);
        // If the event loop is closed, it has been closed this iteration and now the closing
        // event should be emitted
        if self.closed() {
            self.handle_event(Event::LoopDestroyed, &mut control);
        }
    }

    // handle_event takes in events and either queues them or applies a callback
    //
    // It should only ever be called from send_event
    fn handle_event(&self, event: Event<T>, control: &mut ControlFlow) {
        let closed = self.closed();

        match *self.0.runner.borrow_mut() {
            Some(ref mut runner) => {
                // An event is being processed, so the runner should be marked busy
                runner.is_busy = true;

                (runner.event_handler)(event, control);

                // Maintain closed state, even if the callback changes it
                if closed {
                    *control = ControlFlow::Exit;
                }

                // An event is no longer being processed
                runner.is_busy = false;
            }
            // If an event is being handled without a runner somehow, add it to the event queue so
            // it will eventually be processed
            _ => self.0.events.borrow_mut().push_back(event)
        }

        // Don't take events out of the queue if the loop is closed or the runner doesn't exist
        // If the runner doesn't exist and this method recurses, it will recurse infinitely
        if !closed && self.0.runner.borrow().is_some() {
            // Take an event out of the queue and handle it
            if let Some(event) = self.0.events.borrow_mut().pop_front() {
                self.handle_event(event, control);
            }
        }
    }

    // Apply the new ControlFlow that has been selected by the user
    // Start any necessary timeouts etc
    fn apply_control_flow(&self, control_flow: ControlFlow) {
        let mut control_flow_status = match control_flow {
           ControlFlow::Poll => {
                let cloned = self.clone();
                ControlFlowStatus::Poll {
                    timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), 1)
                }
            }
            ControlFlow::Wait => ControlFlowStatus::Wait { start: Instant::now() },
            ControlFlow::WaitUntil(end) => {
                let cloned = self.clone();
                let start = Instant::now();
                let delay = if end <= start {
                    Duration::from_millis(0)
                } else {
                    end - start
                };
                ControlFlowStatus::WaitUntil {
                    start,
                    end,
                    timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), delay.as_millis() as u32)
                }
            }
            ControlFlow::Exit => ControlFlowStatus::Exit,
        };

        match *self.0.runner.borrow_mut() {
            Some(ref mut runner) => {
                // Put the new control flow status in the runner, and take out the old one
                // This way we can safely take ownership of the TimeoutHandle and clear it,
                // so that we don't get 'ghost' invocations of Poll or WaitUntil from earlier
                // set_timeout invocations
                std::mem::swap(&mut runner.control, &mut control_flow_status);
                match control_flow_status {
                    ControlFlowStatus::Poll { timeout } | ControlFlowStatus::WaitUntil { timeout, .. } => timeout.clear(),
                    _ => (),
                }
            }
            None => ()
        }
    }

    // Check if the event loop is currntly closed
    fn closed(&self) -> bool {
        match *self.0.runner.borrow() {
            Some(ref runner) => runner.control.is_exit(),
            None => false, // If the event loop is None, it has not been intialised yet, so it cannot be closed
        }
    }

    // Get the current control flow state
    fn current_control_flow(&self) -> ControlFlow {
        match *self.0.runner.borrow() {
            Some(ref runner) => runner.control.to_control_flow(),
            None => ControlFlow::Poll,
        }
    }
}

<|endoftext|>


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

use dataflow::{FlowAtLocation, FlowsAtLocation};
use borrow_check::nll::region_infer::Cause;
use dataflow::MaybeInitializedPlaces;
use dataflow::move_paths::{HasMoveData, MoveData};
use rustc::mir::{BasicBlock, Location, Mir};
use rustc::mir::Local;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
//use rustc::ty::subst::Kind;
use rustc::traits;
use rustc::infer::InferOk;
use rustc::util::common::ErrorReported;
use borrow_check::nll::type_check::AtLocation;
use rustc_data_structures::fx::FxHashSet;
use syntax::codemap::DUMMY_SP;
use util::liveness::LivenessResults;

use super::TypeChecker;

/// Combines liveness analysis with initialization analysis to
/// determine which variables are live at which points, both due to
/// ordinary uses and drops. Returns a set of (ty, location) pairs
/// that indicate which types must be live at which point in the CFG.
/// This vector is consumed by `constraint_generation`.
///
/// NB. This computation requires normalization; therefore, it must be
/// performed before
pub(super) fn generate<'gcx, 'tcx>(
    cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
    mir: &Mir<'tcx>,
    liveness: &LivenessResults,
    flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
    move_data: &MoveData<'tcx>,
) {
    let tcx = cx.tcx();
    let mut generator = TypeLivenessGenerator {
        cx,
        tcx,
        mir,
        liveness,
        flow_inits,
        move_data,
    };

    for bb in mir.basic_blocks().indices() {
        generator.add_liveness_constraints(bb);
    }
}

struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
where
    'typeck: 'gen,
    'flow: 'gen,
    'tcx: 'typeck + 'flow,
    'gcx: 'tcx,
{
    cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
    tcx: TyCtxt<'typeck, 'gcx, 'tcx>,
    mir: &'gen Mir<'tcx>,
    liveness: &'gen LivenessResults,
    flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
    move_data: &'gen MoveData<'tcx>,
}

impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> {
    /// Liveness constraints:
    ///
    /// > If a variable V is live at point P, then all regions R in the type of V
    /// > must include the point P.
    fn add_liveness_constraints(&mut self, bb: BasicBlock) {
        debug!("add_liveness_constraints(bb={:?})", bb);

        self.liveness
            .regular
            .simulate_block(self.mir, bb, |location, live_locals| {
                for live_local in live_locals.iter() {
                    let live_local_ty = self.mir.local_decls[live_local].ty;
                    let cause = Cause::LiveVar(live_local, location);
                    self.push_type_live_constraint(live_local_ty, location, cause);
                }
            });

        let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
        self.liveness
            .drop
            .simulate_block(self.mir, bb, |location, live_locals| {
                all_live_locals.push((location, live_locals.iter().collect()));
            });
        debug!(
            "add_liveness_constraints: all_live_locals={:#?}",
            all_live_locals
        );

        let terminator_index = self.mir.basic_blocks()[bb].statements.len();
        self.flow_inits.reset_to_entry_of(bb);
        while let Some((location, live_locals)) = all_live_locals.pop() {
            for live_local in live_locals {
                debug!(
                    "add_liveness_constraints: location={:?} live_local={:?}",
                    location,
                    live_local
                );

                self.flow_inits.each_state_bit(|mpi_init| {
                    debug!(
                        "add_liveness_constraints: location={:?} initialized={:?}",
                        location,
                        &self.flow_inits.operator().move_data().move_paths[mpi_init]
                    );
                });

                let mpi = self.move_data.rev_lookup.find_local(live_local);
                if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
                    debug!(
                        "add_liveness_constraints: mpi={:?} has initialized child {:?}",
                        self.move_data.move_paths[mpi],
                        self.move_data.move_paths[initialized_child]
                    );

                    let live_local_ty = self.mir.local_decls[live_local].ty;
                    self.add_drop_live_constraint(live_local, live_local_ty, location);
                }
            }

            if location.statement_index == terminator_index {
                debug!(
                    "add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
                    location
                );
                self.flow_inits.reconstruct_terminator_effect(location);
            } else {
                debug!(
                    "add_liveness_constraints: reconstruct_statement_effect from {:#?}",
                    location
                );
                self.flow_inits.reconstruct_statement_effect(location);
            }
            self.flow_inits.apply_local_effect(location);
        }
    }

    /// Some variable with type `live_ty` is "regular live" at
    /// `location` -- i.e., it may be used later. This means that all
    /// regions appearing in the type `live_ty` must be live at
    /// `location`.
    fn push_type_live_constraint<T>(&mut self, value: T, location: Location, cause: Cause)
    where
        T: TypeFoldable<'tcx>,
    {
        debug!(
            "push_type_live_constraint(live_ty={:?}, location={:?})",
            value,
            location
        );

        self.tcx.for_each_free_region(&value, |live_region| {
            self.cx
                .constraints
                .liveness_set
                .push((live_region, location, cause.clone()));
        });
    }

    /// Some variable with type `live_ty` is "drop live" at `location`
    /// -- i.e., it may be dropped later. This means that *some* of
    /// the regions in its type must be live at `location`. The
    /// precise set will depend on the dropck constraints, and in
    /// particular this takes `#[may_dangle]` into account.
    fn add_drop_live_constraint(
        &mut self,
        dropped_local: Local,
        dropped_ty: Ty<'tcx>,
        location: Location,
    ) {
        debug!(
            "add_drop_live_constraint(dropped_local={:?}, dropped_ty={:?}, location={:?})",
            dropped_local,
            dropped_ty,
            location
        );

        // If we end visiting the same type twice (usually due to a cycle involving
        // associated types), we need to ensure that its region types match up with the type
        // we added to the 'known' map the first time around. For this reason, we need
        // our infcx to hold onto its calculated region constraints after each call
        // to dtorck_constraint_for_ty. Otherwise, normalizing the corresponding associated
        // type will end up instantiating the type with a new set of inference variables
        // Since this new type will never be in 'known', we end up looping forever.
        //
        // For this reason, we avoid calling TypeChecker.normalize, instead doing all normalization
        // ourselves in one large 'fully_perform_op' callback.
        let (type_constraints, kind_constraints) = self.cx.fully_perform_op(location.at_self(),
            |cx| {

            let tcx = cx.infcx.tcx;
            let mut selcx = traits::SelectionContext::new(cx.infcx);
            let cause = cx.misc(cx.last_span);

            let mut types = vec![(dropped_ty, 0)];
            let mut final_obligations = Vec::new();
            let mut type_constraints = Vec::new();
            let mut kind_constraints = Vec::new();

            let mut known = FxHashSet();

            while let Some((ty, depth)) = types.pop() {
                let span = DUMMY_SP; // FIXME
                let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
                    Ok(result) => result,
                    Err(ErrorReported) => {
                        continue;
                    }
                };

                let ty::DtorckConstraint {
                    outlives,
                    dtorck_types,
                } = result;

                // All things in the `outlives` array may be touched by
                // the destructor and must be live at this point.
                for outlive in outlives {
                    let cause = Cause::DropVar(dropped_local, location);
                    kind_constraints.push((outlive, location, cause));
                }

                // However, there may also be some types that
                // `dtorck_constraint_for_ty` could not resolve (e.g.,
                // associated types and parameters). We need to normalize
                // associated types here and possibly recursively process.
                for ty in dtorck_types {
                    let traits::Normalized { value: ty, obligations } =
                        traits::normalize(&mut selcx, cx.param_env, cause.clone(), &ty);

                    final_obligations.extend(obligations);

                    //let ty = self.cx.normalize(&ty, location);
                    let ty = cx.infcx.resolve_type_and_region_vars_if_possible(&ty);
                    match ty.sty {
                        ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
                            let cause = Cause::DropVar(dropped_local, location);
                            type_constraints.push((ty, location, cause));
                        }

                        _ => if known.insert(ty) {
                            types.push((ty, depth + 1));
                        },
                    }
                }
            }

            Ok(InferOk {
                value: (type_constraints, kind_constraints), obligations: final_obligations
            })
        }).unwrap();

        for (ty, location, cause) in type_constraints {
            self.push_type_live_constraint(ty, location, cause);
        }

        for (kind, location, cause) in kind_constraints {
            self.push_type_live_constraint(kind, location, cause);
        }
    }
}
<commit_msg>Remove commented-out code<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use dataflow::{FlowAtLocation, FlowsAtLocation};
use borrow_check::nll::region_infer::Cause;
use dataflow::MaybeInitializedPlaces;
use dataflow::move_paths::{HasMoveData, MoveData};
use rustc::mir::{BasicBlock, Location, Mir};
use rustc::mir::Local;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::traits;
use rustc::infer::InferOk;
use rustc::util::common::ErrorReported;
use borrow_check::nll::type_check::AtLocation;
use rustc_data_structures::fx::FxHashSet;
use syntax::codemap::DUMMY_SP;
use util::liveness::LivenessResults;

use super::TypeChecker;

/// Combines liveness analysis with initialization analysis to
/// determine which variables are live at which points, both due to
/// ordinary uses and drops. Returns a set of (ty, location) pairs
/// that indicate which types must be live at which point in the CFG.
/// This vector is consumed by `constraint_generation`.
///
/// NB. This computation requires normalization; therefore, it must be
/// performed before
pub(super) fn generate<'gcx, 'tcx>(
    cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
    mir: &Mir<'tcx>,
    liveness: &LivenessResults,
    flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
    move_data: &MoveData<'tcx>,
) {
    let tcx = cx.tcx();
    let mut generator = TypeLivenessGenerator {
        cx,
        tcx,
        mir,
        liveness,
        flow_inits,
        move_data,
    };

    for bb in mir.basic_blocks().indices() {
        generator.add_liveness_constraints(bb);
    }
}

struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
where
    'typeck: 'gen,
    'flow: 'gen,
    'tcx: 'typeck + 'flow,
    'gcx: 'tcx,
{
    cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
    tcx: TyCtxt<'typeck, 'gcx, 'tcx>,
    mir: &'gen Mir<'tcx>,
    liveness: &'gen LivenessResults,
    flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
    move_data: &'gen MoveData<'tcx>,
}

impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> {
    /// Liveness constraints:
    ///
    /// > If a variable V is live at point P, then all regions R in the type of V
    /// > must include the point P.
    fn add_liveness_constraints(&mut self, bb: BasicBlock) {
        debug!("add_liveness_constraints(bb={:?})", bb);

        self.liveness
            .regular
            .simulate_block(self.mir, bb, |location, live_locals| {
                for live_local in live_locals.iter() {
                    let live_local_ty = self.mir.local_decls[live_local].ty;
                    let cause = Cause::LiveVar(live_local, location);
                    self.push_type_live_constraint(live_local_ty, location, cause);
                }
            });

        let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
        self.liveness
            .drop
            .simulate_block(self.mir, bb, |location, live_locals| {
                all_live_locals.push((location, live_locals.iter().collect()));
            });
        debug!(
            "add_liveness_constraints: all_live_locals={:#?}",
            all_live_locals
        );

        let terminator_index = self.mir.basic_blocks()[bb].statements.len();
        self.flow_inits.reset_to_entry_of(bb);
        while let Some((location, live_locals)) = all_live_locals.pop() {
            for live_local in live_locals {
                debug!(
                    "add_liveness_constraints: location={:?} live_local={:?}",
                    location,
                    live_local
                );

                self.flow_inits.each_state_bit(|mpi_init| {
                    debug!(
                        "add_liveness_constraints: location={:?} initialized={:?}",
                        location,
                        &self.flow_inits.operator().move_data().move_paths[mpi_init]
                    );
                });

                let mpi = self.move_data.rev_lookup.find_local(live_local);
                if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
                    debug!(
                        "add_liveness_constraints: mpi={:?} has initialized child {:?}",
                        self.move_data.move_paths[mpi],
                        self.move_data.move_paths[initialized_child]
                    );

                    let live_local_ty = self.mir.local_decls[live_local].ty;
                    self.add_drop_live_constraint(live_local, live_local_ty, location);
                }
            }

            if location.statement_index == terminator_index {
                debug!(
                    "add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
                    location
                );
                self.flow_inits.reconstruct_terminator_effect(location);
            } else {
                debug!(
                    "add_liveness_constraints: reconstruct_statement_effect from {:#?}",
                    location
                );
                self.flow_inits.reconstruct_statement_effect(location);
            }
            self.flow_inits.apply_local_effect(location);
        }
    }

    /// Some variable with type `live_ty` is "regular live" at
    /// `location` -- i.e., it may be used later. This means that all
    /// regions appearing in the type `live_ty` must be live at
    /// `location`.
    fn push_type_live_constraint<T>(&mut self, value: T, location: Location, cause: Cause)
    where
        T: TypeFoldable<'tcx>,
    {
        debug!(
            "push_type_live_constraint(live_ty={:?}, location={:?})",
            value,
            location
        );

        self.tcx.for_each_free_region(&value, |live_region| {
            self.cx
                .constraints
                .liveness_set
                .push((live_region, location, cause.clone()));
        });
    }

    /// Some variable with type `live_ty` is "drop live" at `location`
    /// -- i.e., it may be dropped later. This means that *some* of
    /// the regions in its type must be live at `location`. The
    /// precise set will depend on the dropck constraints, and in
    /// particular this takes `#[may_dangle]` into account.
    fn add_drop_live_constraint(
        &mut self,
        dropped_local: Local,
        dropped_ty: Ty<'tcx>,
        location: Location,
    ) {
        debug!(
            "add_drop_live_constraint(dropped_local={:?}, dropped_ty={:?}, location={:?})",
            dropped_local,
            dropped_ty,
            location
        );

        // If we end visiting the same type twice (usually due to a cycle involving
        // associated types), we need to ensure that its region types match up with the type
        // we added to the 'known' map the first time around. For this reason, we need
        // our infcx to hold onto its calculated region constraints after each call
        // to dtorck_constraint_for_ty. Otherwise, normalizing the corresponding associated
        // type will end up instantiating the type with a new set of inference variables
        // Since this new type will never be in 'known', we end up looping forever.
        //
        // For this reason, we avoid calling TypeChecker.normalize, instead doing all normalization
        // ourselves in one large 'fully_perform_op' callback.
        let (type_constraints, kind_constraints) = self.cx.fully_perform_op(location.at_self(),
            |cx| {

            let tcx = cx.infcx.tcx;
            let mut selcx = traits::SelectionContext::new(cx.infcx);
            let cause = cx.misc(cx.last_span);

            let mut types = vec![(dropped_ty, 0)];
            let mut final_obligations = Vec::new();
            let mut type_constraints = Vec::new();
            let mut kind_constraints = Vec::new();

            let mut known = FxHashSet();

            while let Some((ty, depth)) = types.pop() {
                let span = DUMMY_SP; // FIXME
                let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
                    Ok(result) => result,
                    Err(ErrorReported) => {
                        continue;
                    }
                };

                let ty::DtorckConstraint {
                    outlives,
                    dtorck_types,
                } = result;

                // All things in the `outlives` array may be touched by
                // the destructor and must be live at this point.
                for outlive in outlives {
                    let cause = Cause::DropVar(dropped_local, location);
                    kind_constraints.push((outlive, location, cause));
                }

                // However, there may also be some types that
                // `dtorck_constraint_for_ty` could not resolve (e.g.,
                // associated types and parameters). We need to normalize
                // associated types here and possibly recursively process.
                for ty in dtorck_types {
                    let traits::Normalized { value: ty, obligations } =
                        traits::normalize(&mut selcx, cx.param_env, cause.clone(), &ty);

                    final_obligations.extend(obligations);

                    let ty = cx.infcx.resolve_type_and_region_vars_if_possible(&ty);
                    match ty.sty {
                        ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
                            let cause = Cause::DropVar(dropped_local, location);
                            type_constraints.push((ty, location, cause));
                        }

                        _ => if known.insert(ty) {
                            types.push((ty, depth + 1));
                        },
                    }
                }
            }

            Ok(InferOk {
                value: (type_constraints, kind_constraints), obligations: final_obligations
            })
        }).unwrap();

        for (ty, location, cause) in type_constraints {
            self.push_type_live_constraint(ty, location, cause);
        }

        for (kind, location, cause) in kind_constraints {
            self.push_type_live_constraint(kind, location, cause);
        }
    }
}
<|endoftext|>


<commit_before>// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[link_args = "-lpthread -lstdc++"]
#[nolink]
extern mod m { }

#[cfg(target_os = "macos")]
#[link_args = "-L. -lstdc++"]
#[nolink]
extern mod m { }
<commit_msg>Link to zlib on os x<commit_after>// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[link_args = "-lpthread -lstdc++"]
#[nolink]
extern mod m { }

#[cfg(target_os = "macos")]
#[link_args = "-L. -lstdc++ -lz"]
#[nolink]
extern mod m { }
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::{aws::Aws, cluster::Cluster};
use anyhow::{bail, format_err, Result};
use rusoto_core::RusotoError;
use rusoto_ecr::{
    BatchGetImageRequest, DescribeImagesRequest, DescribeImagesResponse, Ecr, Image,
    ImageIdentifier, PutImageError, PutImageRequest,
};
use rusoto_ecs::{Ecs, UpdateServiceRequest};
use slog_scope::{info, warn};
use std::{env, thread, time::Duration};
use util::retry;

#[derive(Clone)]
pub struct DeploymentManager {
    aws: Aws,
    cluster: Cluster,
    running_tag: String,
}

const VALIDATOR_IMAGE_REPO: &str = "libra_validator";
const NIGHTLY_PREFIX: &str = "nightly";
const UPSTREAM_PREFIX: &str = "upstream_";
const MASTER_PREFIX: &str = "master_";

impl DeploymentManager {
    pub fn new(aws: Aws, cluster: Cluster) -> Self {
        let running_tag = env::var("TAG").unwrap_or_else(|_| aws.workspace().to_string());
        if running_tag.starts_with(NIGHTLY_PREFIX)
            || running_tag.starts_with(UPSTREAM_PREFIX)
            || running_tag.starts_with(MASTER_PREFIX)
        {
            panic!(
                "Cluster test can not deploy if workspace is configured to use {} tag.\
                 Use custom tag for your workspace to use --deploy",
                running_tag
            );
        }
        info!("Will use {} tag for deployment", running_tag);
        Self {
            aws,
            cluster,
            running_tag,
        }
    }

    pub fn redeploy(&mut self, hash: String) -> Result<()> {
        info!("Will deploy with digest {}", hash);
        self.tag_image(
            VALIDATOR_IMAGE_REPO,
            &ImageIdentifier {
                image_digest: Some(hash),
                image_tag: None,
            },
            &self.running_tag,
        )?;
        self.update_all_services()?;
        Ok(())
    }

    pub fn update_all_services(&self) -> Result<()> {
        for instance in self.cluster.instances() {
            let mut request = UpdateServiceRequest::default();
            request.cluster = Some(self.aws.workspace().clone());
            request.force_new_deployment = Some(true);
            request.service = format!(
                "{w}/{w}-validator-{hash}",
                w = self.aws.workspace(),
                hash = instance.peer_name()
            );

            self.aws
                .ecs()
                .update_service(request)
                .sync()
                .map_err(|e| format_err!("Failed to update {}: {:?}", instance, e))?;
            thread::sleep(Duration::from_millis(100));
        }
        Ok(())
    }

    fn image_digest_by_tag(&self, tag: &str) -> Result<String> {
        let result = self.describe_images(tag);
        let images = result
            .image_details
            .ok_or_else(|| format_err!("No image_details in ECR response"))?;
        if images.len() != 1 {
            bail!("Ecr returned {} images for libra_e2e:nightly", images.len());
        }
        let image = images.into_iter().next().unwrap();
        image
            .image_digest
            .ok_or_else(|| format_err!("No image_digest"))
    }

    fn describe_images(&self, tag: &str) -> DescribeImagesResponse {
        let mut retry = 0usize;
        loop {
            let mut request = DescribeImagesRequest::default();
            request.repository_name = VALIDATOR_IMAGE_REPO.into();
            let image_id = ImageIdentifier {
                image_digest: None,
                image_tag: Some(tag.to_string()),
            };
            request.image_ids = Some(vec![image_id]);
            match self.aws.ecr().describe_images(request).sync() {
                Ok(r) => return r,
                Err(e) => {
                    if retry > 10 {
                        panic!("Failed describe_images after 10 attempts");
                    } else {
                        warn!("Transient failure in describe_images: {}", e);
                        thread::sleep(Duration::from_secs(10));
                        retry += 1;
                    }
                }
            }
        }
    }

    fn get_images(&self, repository: &str, image_id: &ImageIdentifier) -> Result<Vec<Image>> {
        let mut get_request = BatchGetImageRequest::default();
        get_request.repository_name = repository.to_string();
        get_request.image_ids = vec![image_id.clone()];
        // Retry upto 10 times, waiting 10 sec between retries
        let response = retry::retry(retry::fixed_retry_strategy(10_000, 10), || {
            self.aws
                .ecr()
                .batch_get_image(get_request.clone())
                .sync()
                .map_err(|e| {
                    warn!(
                        "Failed to get image from repository: {:?}. Retrying...",
                        get_request.repository_name
                    );
                    e
                })
        })
        .map_err(|e| {
            format_err!(
                "Failed to get image from repository: {:?} after 10 tries: {:?}",
                get_request.repository_name,
                e
            )
        })?;
        response
            .images
            .ok_or_else(|| format_err!("No images in batch_get_image response"))
    }

    fn tag_image(&self, repository: &str, image_id: &ImageIdentifier, new_tag: &str) -> Result<()> {
        let images = self.get_images(repository, &image_id)?;
        let image = images
            .into_iter()
            .next()
            .ok_or_else(|| format_err!("get_images returned 0 images"))?;
        let manifest = image
            .image_manifest
            .ok_or_else(|| format_err!("no manifest in batch_get_image response"))?;
        let mut put_request = PutImageRequest::default();
        put_request.image_manifest = manifest;
        put_request.repository_name = repository.to_string();
        put_request.image_tag = Some(new_tag.to_string());
        let result = self.aws.ecr().put_image(put_request).sync();
        if let Err(e) = result {
            if let RusotoError::Service(PutImageError::ImageAlreadyExists(_)) = e {
                info!("Tag for Image already exists {}:{}", repository, new_tag);
                Ok(())
            } else {
                Err(format_err!("Failed to tag image: {:?}", e))
            }
        } else {
            Ok(())
        }
    }

    pub fn resolve(&self, hash_or_tag: &str) -> Result<String> {
        if hash_or_tag.starts_with("sha256:") {
            Ok(hash_or_tag.to_string())
        } else {
            self.image_digest_by_tag(hash_or_tag)
        }
    }
}
<commit_msg>[cluster-test] Update service name used in deployment<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::{aws::Aws, cluster::Cluster};
use anyhow::{bail, format_err, Result};
use rusoto_core::RusotoError;
use rusoto_ecr::{
    BatchGetImageRequest, DescribeImagesRequest, DescribeImagesResponse, Ecr, Image,
    ImageIdentifier, PutImageError, PutImageRequest,
};
use rusoto_ecs::{Ecs, UpdateServiceRequest};
use slog_scope::{info, warn};
use std::{env, thread, time::Duration};
use util::retry;

#[derive(Clone)]
pub struct DeploymentManager {
    aws: Aws,
    cluster: Cluster,
    running_tag: String,
}

const VALIDATOR_IMAGE_REPO: &str = "libra_validator";
const NIGHTLY_PREFIX: &str = "nightly";
const UPSTREAM_PREFIX: &str = "upstream_";
const MASTER_PREFIX: &str = "master_";

impl DeploymentManager {
    pub fn new(aws: Aws, cluster: Cluster) -> Self {
        let running_tag = env::var("TAG").unwrap_or_else(|_| aws.workspace().to_string());
        if running_tag.starts_with(NIGHTLY_PREFIX)
            || running_tag.starts_with(UPSTREAM_PREFIX)
            || running_tag.starts_with(MASTER_PREFIX)
        {
            panic!(
                "Cluster test can not deploy if workspace is configured to use {} tag.\
                 Use custom tag for your workspace to use --deploy",
                running_tag
            );
        }
        info!("Will use {} tag for deployment", running_tag);
        Self {
            aws,
            cluster,
            running_tag,
        }
    }

    pub fn redeploy(&mut self, hash: String) -> Result<()> {
        info!("Will deploy with digest {}", hash);
        self.tag_image(
            VALIDATOR_IMAGE_REPO,
            &ImageIdentifier {
                image_digest: Some(hash),
                image_tag: None,
            },
            &self.running_tag,
        )?;
        self.update_all_services()?;
        Ok(())
    }

    pub fn update_all_services(&self) -> Result<()> {
        for instance in self.cluster.instances() {
            let mut request = UpdateServiceRequest::default();
            request.cluster = Some(self.aws.workspace().clone());
            request.force_new_deployment = Some(true);
            request.service = instance.peer_name().to_string();

            self.aws
                .ecs()
                .update_service(request)
                .sync()
                .map_err(|e| format_err!("Failed to update {}: {:?}", instance, e))?;
            thread::sleep(Duration::from_millis(200));
        }
        Ok(())
    }

    fn image_digest_by_tag(&self, tag: &str) -> Result<String> {
        let result = self.describe_images(tag);
        let images = result
            .image_details
            .ok_or_else(|| format_err!("No image_details in ECR response"))?;
        if images.len() != 1 {
            bail!("Ecr returned {} images for libra_e2e:nightly", images.len());
        }
        let image = images.into_iter().next().unwrap();
        image
            .image_digest
            .ok_or_else(|| format_err!("No image_digest"))
    }

    fn describe_images(&self, tag: &str) -> DescribeImagesResponse {
        let mut retry = 0usize;
        loop {
            let mut request = DescribeImagesRequest::default();
            request.repository_name = VALIDATOR_IMAGE_REPO.into();
            let image_id = ImageIdentifier {
                image_digest: None,
                image_tag: Some(tag.to_string()),
            };
            request.image_ids = Some(vec![image_id]);
            match self.aws.ecr().describe_images(request).sync() {
                Ok(r) => return r,
                Err(e) => {
                    if retry > 10 {
                        panic!("Failed describe_images after 10 attempts");
                    } else {
                        warn!("Transient failure in describe_images: {}", e);
                        thread::sleep(Duration::from_secs(10));
                        retry += 1;
                    }
                }
            }
        }
    }

    fn get_images(&self, repository: &str, image_id: &ImageIdentifier) -> Result<Vec<Image>> {
        let mut get_request = BatchGetImageRequest::default();
        get_request.repository_name = repository.to_string();
        get_request.image_ids = vec![image_id.clone()];
        // Retry upto 10 times, waiting 10 sec between retries
        let response = retry::retry(retry::fixed_retry_strategy(10_000, 10), || {
            self.aws
                .ecr()
                .batch_get_image(get_request.clone())
                .sync()
                .map_err(|e| {
                    warn!(
                        "Failed to get image from repository: {:?}. Retrying...",
                        get_request.repository_name
                    );
                    e
                })
        })
        .map_err(|e| {
            format_err!(
                "Failed to get image from repository: {:?} after 10 tries: {:?}",
                get_request.repository_name,
                e
            )
        })?;
        response
            .images
            .ok_or_else(|| format_err!("No images in batch_get_image response"))
    }

    fn tag_image(&self, repository: &str, image_id: &ImageIdentifier, new_tag: &str) -> Result<()> {
        let images = self.get_images(repository, &image_id)?;
        let image = images
            .into_iter()
            .next()
            .ok_or_else(|| format_err!("get_images returned 0 images"))?;
        let manifest = image
            .image_manifest
            .ok_or_else(|| format_err!("no manifest in batch_get_image response"))?;
        let mut put_request = PutImageRequest::default();
        put_request.image_manifest = manifest;
        put_request.repository_name = repository.to_string();
        put_request.image_tag = Some(new_tag.to_string());
        let result = self.aws.ecr().put_image(put_request).sync();
        if let Err(e) = result {
            if let RusotoError::Service(PutImageError::ImageAlreadyExists(_)) = e {
                info!("Tag for Image already exists {}:{}", repository, new_tag);
                Ok(())
            } else {
                Err(format_err!("Failed to tag image: {:?}", e))
            }
        } else {
            Ok(())
        }
    }

    pub fn resolve(&self, hash_or_tag: &str) -> Result<String> {
        if hash_or_tag.starts_with("sha256:") {
            Ok(hash_or_tag.to_string())
        } else {
            self.image_digest_by_tag(hash_or_tag)
        }
    }
}
<|endoftext|>


<commit_before>use super::Scratchpad;
use crate::StrError;
use russell_lab::{inverse, mat_mat_mul};

/// Indicates that the determinant of the Jacobian is not available (e.g., Shells)
///
/// **Note:** This also indicates that the inverse Jacobian matrix has not been computed
pub const DET_JAC_NOT_AVAILABLE: f64 = -1.0;

impl Scratchpad {
    /// Calculates the Jacobian of the mapping from general to reference space
    ///
    /// The components of the Jacobian matrix are
    ///
    /// ```text
    ///        ∂xᵢ
    /// Jᵢⱼ := ——— = Σ X[m][i] * L[m][j]
    ///        ∂ξⱼ   m
    /// ```
    ///
    /// Thus, in matrix notation
    ///
    /// ```text
    /// jacobian := J = Xᵀ · L
    /// jacobian := Jcable = Xᵀ · L
    /// jacobian := Jshell = Xᵀ · L
    /// ```
    ///
    /// where:
    ///
    /// * `Jcable`` -- Jacobian for line in multi-dimensions (geom_ndim < space_ndim)
    /// * `Jshell`` -- Jacobian for 3D surfaces (geo_ndim = 2 and space_ndim = 3)
    ///
    /// For the `SOLID` case (`geo_ndim = space_ndim`), this function also computes the inverse Jacobian:
    ///
    /// ```text
    /// inv_jacobian := J⁻¹
    /// ```
    ///
    /// # Output
    ///
    /// * `pad.deriv` -- derivatives of the interpolation functions (nnode); `L` matrix
    /// * `pad.jacobian` -- Jacobian matrix (space_ndim,geo_ndim)
    /// * `pad.inv_jacobian` -- If `geo_ndim = space_ndim` (`SOLID` case): inverse Jacobian matrix (space_ndim,space_ndim)
    /// * Returns one of the following:
    ///     * `CABLE`: (geo_ndim = 1 and space_ndim = 2 or 3), returns the norm of the Jacobian vector
    ///     * `SHELL`: (geo_ndim = 2 and space_ndim = 3), returns [DET_JAC_NOT_AVAILABLE] indicating that the
    ///        determinant of the Jacobian is not available and the inverse Jacobian has not been computed
    ///     * `SOLID`: (geo_ndim = space_ndim), returns the determinant of the Jacobian
    ///
    /// # Input
    ///
    /// * `ksi` -- reference coordinates ξ with len ≥ geo_ndim
    ///
    /// # Examples
    ///
    /// ```
    /// use gemlab::shapes::{GeoKind, Scratchpad};
    /// use gemlab::StrError;
    /// use russell_chk::assert_approx_eq;
    ///
    /// fn main() -> Result<(), StrError> {
    ///     //  3-------------2         ξ₀   ξ₁
    ///     //  |      ξ₁     |  node    r    s
    ///     //  |      |      |     0 -1.0 -1.0
    ///     //  |      +--ξ₀  |     1  1.0 -1.0
    ///     //  |             |     2  1.0  1.0
    ///     //  |             |     3 -1.0  1.0
    ///     //  0-------------1
    ///
    ///     let a = 3.0;
    ///     let space_ndim = 2;
    ///     let mut pad = Scratchpad::new(space_ndim, GeoKind::Qua4)?;
    ///     pad.set_xx(0, 0, 0.0);
    ///     pad.set_xx(0, 1, 0.0);
    ///     pad.set_xx(1, 0, 2.0 * a);
    ///     pad.set_xx(1, 1, 0.0);
    ///     pad.set_xx(2, 0, 2.0 * a);
    ///     pad.set_xx(2, 1, a);
    ///     pad.set_xx(3, 0, 0.0);
    ///     pad.set_xx(3, 1, a);
    ///
    ///     let det_jac = pad.calc_jacobian(&[0.0, 0.0])?;
    ///     assert_approx_eq!(det_jac, a * a / 2.0, 1e-15);
    ///
    ///     // the solution is
    ///     //  ┌         ┐
    ///     //  │  a   0  │
    ///     //  │  0  a/2 │
    ///     //  └         ┘
    ///     assert_eq!(
    ///         format!("{}", pad.jacobian),
    ///         "┌         ┐\n\
    ///          │   3   0 │\n\
    ///          │   0 1.5 │\n\
    ///          └         ┘"
    ///     );
    ///     Ok(())
    /// }
    /// ```
    pub fn calc_jacobian(&mut self, ksi: &[f64]) -> Result<f64, StrError> {
        // check
        if !self.ok_xxt {
            return Err("all components of the coordinates matrix must be set first");
        }

        // matrix L: dNᵐ/dξ
        (self.fn_deriv)(&mut self.deriv, ksi);

        // matrix J: dx/dξ
        mat_mat_mul(&mut self.jacobian, 1.0, &self.xxt, &self.deriv)?;

        // inverse Jacobian and determinant/norm (or not possible)
        let (space_ndim, geo_ndim) = self.jacobian.dims();
        if geo_ndim == space_ndim {
            // SOLID case: inverse J (returns determinant)
            inverse(&mut self.inv_jacobian, &self.jacobian)
        } else {
            // CABLE case: norm of Jacobian vector
            if geo_ndim == 1 {
                let mut norm_jac = 0.0;
                for i in 0..space_ndim {
                    norm_jac += self.jacobian[i][0] * self.jacobian[i][0];
                }
                Ok(f64::sqrt(norm_jac))
            } else {
                // SHELL case
                Ok(DET_JAC_NOT_AVAILABLE)
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::DET_JAC_NOT_AVAILABLE;
    use crate::shapes::scratchpad_testing::aux;
    use crate::shapes::{GeoKind, Scratchpad};
    use crate::StrError;
    use russell_chk::assert_deriv_approx_eq;
    use russell_lab::{Matrix, Vector};

    #[test]
    fn calc_jacobian_handles_errors() {
        let mut pad = Scratchpad::new(2, GeoKind::Tri3).unwrap();
        assert_eq!(
            pad.calc_jacobian(&[0.0, 0.0]).err(),
            Some("all components of the coordinates matrix must be set first")
        );

        // bugged Jacobian matrix
        // (this would only happen if the user messes up the properties of Scratchpad directly)
        pad.set_xx(2, 1, 0.0); // setting the last component
                               // (cannot really check that all components have been set)
        pad.jacobian = Matrix::new(0, 0);
        assert_eq!(pad.calc_jacobian(&[0.0, 0.0]).err(), Some("matrices are incompatible"));
    }

    // Holds arguments for numerical differentiation of x with respect to ξ => Jacobian
    struct ArgsNumJac {
        pad: Scratchpad,  // scratchpad to send to calc_coords
        at_ksi: Vec<f64>, // at reference coord value
        ksi: Vec<f64>,    // temporary reference coord
        x: Vector,        // (space_ndim) coordinates at ξ
        i: usize,         // dimension index from 0 to space_ndim
        j: usize,         // dimension index from 0 to geo_ndim
    }

    // Computes xᵢ(ξ) with variable v := ξⱼ
    fn x_given_ksi(v: f64, args: &mut ArgsNumJac) -> f64 {
        args.ksi.copy_from_slice(&args.at_ksi);
        args.ksi[args.j] = v;
        args.pad.calc_coords(&mut args.x, &args.ksi).unwrap();
        args.x[args.i]
    }

    #[test]
    fn calc_jacobian_works() -> Result<(), StrError> {
        // kind and tolerances
        let problem = vec![
            // Lin
            (GeoKind::Lin2, 1e-12),
            (GeoKind::Lin3, 1e-11),
            (GeoKind::Lin4, 1e-11),
            (GeoKind::Lin5, 1e-11),
            // Tri
            (GeoKind::Tri3, 1e-11),
            (GeoKind::Tri6, 1e-11),
            (GeoKind::Tri10, 1e-10),
            (GeoKind::Tri15, 1e-10),
            // Qua
            (GeoKind::Qua4, 1e-11),
            (GeoKind::Qua8, 1e-11),
            (GeoKind::Qua9, 1e-12),
            (GeoKind::Qua12, 1e-10),
            (GeoKind::Qua16, 1e-10),
            (GeoKind::Qua17, 1e-10),
            // Tet
            (GeoKind::Tet4, 1e-12),
            (GeoKind::Tet10, 1e-11),
            (GeoKind::Tet20, 1e-10),
            // Hex
            (GeoKind::Hex8, 1e-11),
            (GeoKind::Hex20, 1e-11),
            (GeoKind::Hex32, 1e-9),
        ];
        assert_eq!(problem.len(), GeoKind::VALUES.len());

        // loop over shapes
        for (kind, tol) in problem {
            // scratchpad with coordinates
            let geo_ndim = kind.ndim();
            let space_ndim = usize::max(2, geo_ndim);
            let mut pad = aux::gen_scratchpad_with_coords(space_ndim, kind);

            // set ξ within reference space
            let at_ksi = vec![0.25; geo_ndim];

            // compute Jacobian, its inverse, and determinant
            let det_jac = pad.calc_jacobian(&at_ksi)?;
            assert!(det_jac > 0.0);

            // set arguments for numerical integration
            let args = &mut ArgsNumJac {
                pad: pad.clone(),
                at_ksi,
                ksi: vec![0.0; geo_ndim],
                x: Vector::new(space_ndim),
                i: 0,
                j: 0,
            };

            // check J(ξ) = dx(ξ)/dξ
            for i in 0..space_ndim {
                args.i = i;
                for j in 0..geo_ndim {
                    args.j = j;
                    // Jᵢⱼ := dxᵢ/dξⱼ
                    assert_deriv_approx_eq!(pad.jacobian[i][j], args.at_ksi[j], x_given_ksi, args, tol);
                }
            }
        }
        Ok(())
    }

    #[test]
    fn calc_jacobian_special_cases_work() -> Result<(), StrError> {
        // CABLE: line in 2d
        let space_ndim = 2;
        let mut pad = Scratchpad::new(space_ndim, GeoKind::Lin2)?;
        let l = 3.5;
        pad.set_xx(0, 0, 0.0); // node 0
        pad.set_xx(0, 1, 0.0);
        pad.set_xx(1, 0, l); // node 1
        pad.set_xx(1, 1, 0.0);
        let norm_jac_vec = pad.calc_jacobian(&[0.0])?;
        assert_eq!(norm_jac_vec, l / 2.0); // 2.0 = length of shape in the reference space

        // SHELL: triangle on a plane diagonal to the y-z plane
        let space_ndim = 3;
        let mut pad = Scratchpad::new(space_ndim, GeoKind::Tri3)?;
        pad.set_xx(0, 0, 0.0); // node 0
        pad.set_xx(0, 1, 0.0);
        pad.set_xx(0, 2, 0.0);
        pad.set_xx(1, 0, 1.0); // node 1
        pad.set_xx(1, 1, 0.0);
        pad.set_xx(1, 2, 0.0);
        pad.set_xx(2, 0, 0.5); // node 2
        pad.set_xx(2, 1, 1.0);
        pad.set_xx(2, 2, 1.0);
        let norm_jac_vec = pad.calc_jacobian(&[0.0, 0.0])?;
        assert_eq!(norm_jac_vec, DET_JAC_NOT_AVAILABLE);
        if false {
            pad.draw_shape_simple("/tmp/gemlab/test_jacobian_tri3_in_3d.svg")?;
        }
        Ok(())
    }
}
<commit_msg>Improve doc comment<commit_after>use super::Scratchpad;
use crate::StrError;
use russell_lab::{inverse, mat_mat_mul};

/// Indicates that the determinant of the Jacobian is not available (e.g., Shells)
///
/// **Note:** This also indicates that the inverse Jacobian matrix has not been computed
pub const DET_JAC_NOT_AVAILABLE: f64 = -1.0;

impl Scratchpad {
    /// Calculates the Jacobian of the mapping from general to reference space
    ///
    /// The components of the Jacobian matrix are
    ///
    /// ```text
    ///        ∂xᵢ
    /// Jᵢⱼ := ——— = Σ X[m][i] * L[m][j]
    ///        ∂ξⱼ   m
    /// ```
    ///
    /// Thus, in matrix notation
    ///
    /// ```text
    /// jacobian := J = Xᵀ · L
    /// jacobian := Jcable = Xᵀ · L
    /// jacobian := Jshell = Xᵀ · L
    /// ```
    ///
    /// where:
    ///
    /// * `Jcable`` -- Jacobian for line in multi-dimensions (geom_ndim < space_ndim)
    /// * `Jshell`` -- Jacobian for 3D surfaces (geo_ndim = 2 and space_ndim = 3)
    ///
    /// For the `SOLID` case (`geo_ndim = space_ndim`), this function also computes the inverse Jacobian:
    ///
    /// ```text
    /// inv_jacobian := J⁻¹
    /// ```
    ///
    /// # Output
    ///
    /// * `deriv` -- derivatives of the interpolation functions (nnode); `L` matrix
    /// * `jacobian` -- Jacobian matrix (space_ndim,geo_ndim)
    /// * `inv_jacobian` -- If `geo_ndim = space_ndim` (`SOLID` case): inverse Jacobian matrix (space_ndim,space_ndim)
    /// * Returns one of the following:
    ///     * `CABLE`: (geo_ndim = 1 and space_ndim = 2 or 3), returns the norm of the Jacobian vector
    ///     * `SHELL`: (geo_ndim = 2 and space_ndim = 3), returns [DET_JAC_NOT_AVAILABLE] indicating that the
    ///        determinant of the Jacobian is not available and the inverse Jacobian has not been computed
    ///     * `SOLID`: (geo_ndim = space_ndim), returns the determinant of the Jacobian
    ///
    /// # Input
    ///
    /// * `ksi` -- reference coordinates ξ with len ≥ geo_ndim
    ///
    /// # Examples
    ///
    /// ```
    /// use gemlab::shapes::{GeoKind, Scratchpad};
    /// use gemlab::StrError;
    /// use russell_chk::assert_approx_eq;
    ///
    /// fn main() -> Result<(), StrError> {
    ///     //  3-------------2         ξ₀   ξ₁
    ///     //  |      ξ₁     |  node    r    s
    ///     //  |      |      |     0 -1.0 -1.0
    ///     //  |      +--ξ₀  |     1  1.0 -1.0
    ///     //  |             |     2  1.0  1.0
    ///     //  |             |     3 -1.0  1.0
    ///     //  0-------------1
    ///
    ///     let a = 3.0;
    ///     let space_ndim = 2;
    ///     let mut pad = Scratchpad::new(space_ndim, GeoKind::Qua4)?;
    ///     pad.set_xx(0, 0, 0.0);
    ///     pad.set_xx(0, 1, 0.0);
    ///     pad.set_xx(1, 0, 2.0 * a);
    ///     pad.set_xx(1, 1, 0.0);
    ///     pad.set_xx(2, 0, 2.0 * a);
    ///     pad.set_xx(2, 1, a);
    ///     pad.set_xx(3, 0, 0.0);
    ///     pad.set_xx(3, 1, a);
    ///
    ///     let det_jac = pad.calc_jacobian(&[0.0, 0.0])?;
    ///     assert_approx_eq!(det_jac, a * a / 2.0, 1e-15);
    ///
    ///     // the solution is
    ///     //  ┌         ┐
    ///     //  │  a   0  │
    ///     //  │  0  a/2 │
    ///     //  └         ┘
    ///     assert_eq!(
    ///         format!("{}", pad.jacobian),
    ///         "┌         ┐\n\
    ///          │   3   0 │\n\
    ///          │   0 1.5 │\n\
    ///          └         ┘"
    ///     );
    ///     Ok(())
    /// }
    /// ```
    pub fn calc_jacobian(&mut self, ksi: &[f64]) -> Result<f64, StrError> {
        // check
        if !self.ok_xxt {
            return Err("all components of the coordinates matrix must be set first");
        }

        // matrix L: dNᵐ/dξ
        (self.fn_deriv)(&mut self.deriv, ksi);

        // matrix J: dx/dξ
        mat_mat_mul(&mut self.jacobian, 1.0, &self.xxt, &self.deriv)?;

        // inverse Jacobian and determinant/norm (or not possible)
        let (space_ndim, geo_ndim) = self.jacobian.dims();
        if geo_ndim == space_ndim {
            // SOLID case: inverse J (returns determinant)
            inverse(&mut self.inv_jacobian, &self.jacobian)
        } else {
            // CABLE case: norm of Jacobian vector
            if geo_ndim == 1 {
                let mut norm_jac = 0.0;
                for i in 0..space_ndim {
                    norm_jac += self.jacobian[i][0] * self.jacobian[i][0];
                }
                Ok(f64::sqrt(norm_jac))
            } else {
                // SHELL case
                Ok(DET_JAC_NOT_AVAILABLE)
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::DET_JAC_NOT_AVAILABLE;
    use crate::shapes::scratchpad_testing::aux;
    use crate::shapes::{GeoKind, Scratchpad};
    use crate::StrError;
    use russell_chk::assert_deriv_approx_eq;
    use russell_lab::{Matrix, Vector};

    #[test]
    fn calc_jacobian_handles_errors() {
        let mut pad = Scratchpad::new(2, GeoKind::Tri3).unwrap();
        assert_eq!(
            pad.calc_jacobian(&[0.0, 0.0]).err(),
            Some("all components of the coordinates matrix must be set first")
        );

        // bugged Jacobian matrix
        // (this would only happen if the user messes up the properties of Scratchpad directly)
        pad.set_xx(2, 1, 0.0); // setting the last component
                               // (cannot really check that all components have been set)
        pad.jacobian = Matrix::new(0, 0);
        assert_eq!(pad.calc_jacobian(&[0.0, 0.0]).err(), Some("matrices are incompatible"));
    }

    // Holds arguments for numerical differentiation of x with respect to ξ => Jacobian
    struct ArgsNumJac {
        pad: Scratchpad,  // scratchpad to send to calc_coords
        at_ksi: Vec<f64>, // at reference coord value
        ksi: Vec<f64>,    // temporary reference coord
        x: Vector,        // (space_ndim) coordinates at ξ
        i: usize,         // dimension index from 0 to space_ndim
        j: usize,         // dimension index from 0 to geo_ndim
    }

    // Computes xᵢ(ξ) with variable v := ξⱼ
    fn x_given_ksi(v: f64, args: &mut ArgsNumJac) -> f64 {
        args.ksi.copy_from_slice(&args.at_ksi);
        args.ksi[args.j] = v;
        args.pad.calc_coords(&mut args.x, &args.ksi).unwrap();
        args.x[args.i]
    }

    #[test]
    fn calc_jacobian_works() -> Result<(), StrError> {
        // kind and tolerances
        let problem = vec![
            // Lin
            (GeoKind::Lin2, 1e-12),
            (GeoKind::Lin3, 1e-11),
            (GeoKind::Lin4, 1e-11),
            (GeoKind::Lin5, 1e-11),
            // Tri
            (GeoKind::Tri3, 1e-11),
            (GeoKind::Tri6, 1e-11),
            (GeoKind::Tri10, 1e-10),
            (GeoKind::Tri15, 1e-10),
            // Qua
            (GeoKind::Qua4, 1e-11),
            (GeoKind::Qua8, 1e-11),
            (GeoKind::Qua9, 1e-12),
            (GeoKind::Qua12, 1e-10),
            (GeoKind::Qua16, 1e-10),
            (GeoKind::Qua17, 1e-10),
            // Tet
            (GeoKind::Tet4, 1e-12),
            (GeoKind::Tet10, 1e-11),
            (GeoKind::Tet20, 1e-10),
            // Hex
            (GeoKind::Hex8, 1e-11),
            (GeoKind::Hex20, 1e-11),
            (GeoKind::Hex32, 1e-9),
        ];
        assert_eq!(problem.len(), GeoKind::VALUES.len());

        // loop over shapes
        for (kind, tol) in problem {
            // scratchpad with coordinates
            let geo_ndim = kind.ndim();
            let space_ndim = usize::max(2, geo_ndim);
            let mut pad = aux::gen_scratchpad_with_coords(space_ndim, kind);

            // set ξ within reference space
            let at_ksi = vec![0.25; geo_ndim];

            // compute Jacobian, its inverse, and determinant
            let det_jac = pad.calc_jacobian(&at_ksi)?;
            assert!(det_jac > 0.0);

            // set arguments for numerical integration
            let args = &mut ArgsNumJac {
                pad: pad.clone(),
                at_ksi,
                ksi: vec![0.0; geo_ndim],
                x: Vector::new(space_ndim),
                i: 0,
                j: 0,
            };

            // check J(ξ) = dx(ξ)/dξ
            for i in 0..space_ndim {
                args.i = i;
                for j in 0..geo_ndim {
                    args.j = j;
                    // Jᵢⱼ := dxᵢ/dξⱼ
                    assert_deriv_approx_eq!(pad.jacobian[i][j], args.at_ksi[j], x_given_ksi, args, tol);
                }
            }
        }
        Ok(())
    }

    #[test]
    fn calc_jacobian_special_cases_work() -> Result<(), StrError> {
        // CABLE: line in 2d
        let space_ndim = 2;
        let mut pad = Scratchpad::new(space_ndim, GeoKind::Lin2)?;
        let l = 3.5;
        pad.set_xx(0, 0, 0.0); // node 0
        pad.set_xx(0, 1, 0.0);
        pad.set_xx(1, 0, l); // node 1
        pad.set_xx(1, 1, 0.0);
        let norm_jac_vec = pad.calc_jacobian(&[0.0])?;
        assert_eq!(norm_jac_vec, l / 2.0); // 2.0 = length of shape in the reference space

        // SHELL: triangle on a plane diagonal to the y-z plane
        let space_ndim = 3;
        let mut pad = Scratchpad::new(space_ndim, GeoKind::Tri3)?;
        pad.set_xx(0, 0, 0.0); // node 0
        pad.set_xx(0, 1, 0.0);
        pad.set_xx(0, 2, 0.0);
        pad.set_xx(1, 0, 1.0); // node 1
        pad.set_xx(1, 1, 0.0);
        pad.set_xx(1, 2, 0.0);
        pad.set_xx(2, 0, 0.5); // node 2
        pad.set_xx(2, 1, 1.0);
        pad.set_xx(2, 2, 1.0);
        let norm_jac_vec = pad.calc_jacobian(&[0.0, 0.0])?;
        assert_eq!(norm_jac_vec, DET_JAC_NOT_AVAILABLE);
        if false {
            pad.draw_shape_simple("/tmp/gemlab/test_jacobian_tri3_in_3d.svg")?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>fn generate_table () {
    let pi: f64 = 3.1415926536897932384626;
    let k1: f64 = 0.6072529350088812561694; // 1/k
    let num_bits: uint = 32;
    //let num_elms: uint = num_bits;
    let mul: uint = 1<<(num_bits-2);

    println!("Cordic sin in rust");
    println!("num bits {}", num_bits);
    println!("mul {}", mul);
    println!("1<<(num_bits-2) {}", ((1i << (num_bits-2)) as f64));
    println!("pi is {}", pi);
    println!("k1 is {}", k1);

    let shift: f64 = 2.0;
    for ii in range(0, num_bits) {
        let ipow: f64 = 1f64/shift.powi(ii as i32);
        let cur: f64 = ipow.atan() * (mul as f64);
        //println!("table values {:.10f}", cur);
        println!("table values 0x{}", std::f64::to_str_hex(cur));
    }
}

fn main() {
    generate_table();
}
<commit_msg>print the table in  a way rust can compile<commit_after>fn generate_table () {
    let pi: f64 = 3.1415926536897932384626;
    let k1: f64 = 0.6072529350088812561694; // 1/k
    let num_bits: uint = 32;
    //let num_elms: uint = num_bits;
    let mul: uint = 1<<(num_bits-2);

    println!("Cordic sin in rust");
    println!("num bits {}", num_bits);
    println!("mul {}", mul);
    println!("1<<(num_bits-2) {}", ((1i << (num_bits-2)) as f64));
    println!("pi is {}", pi);
    println!("k1 is {}", k1);

    println!("let cordic_tab = [");

    let shift: f64 = 2.0;
    for ii in range(0, num_bits) {
        let ipow: f64 = 1f64/shift.powi(ii as i32);
        let cur: f64 = ipow.atan() * (mul as f64);

        if ii % 4 == 0 && ii > 0 {
            println!("");
        }
        if ii == 0 {
            print!("{:0.8f}f64, ", cur);
        } else {
            print!("{:0.8f}, ", cur);
        }
    }

    println!("];");
}

fn sin(theta: f64) {

    let num_bits: uint = 32;
    let mut d: int;
    let z: int = theta as int;
    let cordic_tab = [
        843314856.53262615f64, 497837829.38176435, 263043836.58692065, 133525158.66814968,
        67021686.89696868, 33543515.72887244, 16775850.86663180, 8388437.33958306,
        4194282.66686198, 2097149.33333944, 1048575.66666686, 524287.95833334,
        262143.99479167, 131071.99934896, 65535.99991862, 32767.99998983,
        16383.99999873, 8191.99999984, 4095.99999998, 2048.00000000,
        1024.00000000, 512.00000000, 256.00000000, 128.00000000,
        64.00000000, 32.00000000, 16.00000000, 8.00000000,
        4.00000000, 2.00000000, 1.00000000, 0.50000000];

    for ii in range(0, num_bits) {
        if z >= 0 {
            d = 0;
        } else {
            d = -1;
        }
        println!("{}", d);
    }

}
fn main() {
    generate_table();

    sin(0.5);
}
<|endoftext|>


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

// compile-flags: -O -C no-prepopulate-passes

#![crate_type = "lib"]
#![feature(rustc_attrs)]

// CHECK-LABEL: @test
#[no_mangle]
#[rustc_mir] // FIXME #27840 MIR has different codegen.
pub fn test() {
    let a = 0;
    &a; // keep variable in an alloca

// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %a to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_a]])

    {
        let b = &Some(a);
        &b; // keep variable in an alloca

// CHECK: [[S_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_b]])

// CHECK: [[S__5:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_5 to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S__5]])

// CHECK: [[E__5:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_5 to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E__5]])

// CHECK: [[E_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_b]])
    }

    let c = 1;
    &c; // keep variable in an alloca

// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %c to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_c]])

// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %c to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_c]])

// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %a to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_a]])
}
<commit_msg>fix codegen test<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-flags: -O -C no-prepopulate-passes

#![crate_type = "lib"]

// CHECK-LABEL: @test
#[no_mangle]
pub fn test() {
    let a = 0;
    &a; // keep variable in an alloca

// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %a to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_a]])

    {
        let b = &Some(a);
        &b; // keep variable in an alloca

// CHECK: [[S_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_b]])

// CHECK: [[S__5:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_5 to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S__5]])

// CHECK: [[E_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_b]])

// CHECK: [[E__5:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_5 to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E__5]])
    }

    let c = 1;
    &c; // keep variable in an alloca

// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %c to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_c]])

// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %c to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_c]])

// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %a to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_a]])
}
<|endoftext|>