<commit_before>// spell-checker:ignore (URL) appspot

use crate::numeric::*;

pub(crate) trait Basis {
    const BASIS: &'static [u64];
}

impl Basis for Montgomery<u64> {
    // Small set of bases for the Miller-Rabin prime test, valid for all 64b integers;
    //  discovered by Jim Sinclair on 2011-04-20, see miller-rabin.appspot.com
    #[allow(clippy::unreadable_literal)]
    const BASIS: &'static [u64] = &[2, 325, 9375, 28178, 450775, 9780504, 1795265022];
}

impl Basis for Montgomery<u32> {
    // Small set of bases for the Miller-Rabin prime test, valid for all 32b integers;
    //  discovered by Steve Worley on 2013-05-27, see miller-rabin.appspot.com
    #[allow(clippy::unreadable_literal)]
    const BASIS: &'static [u64] = &[
        4230279247111683200,
        14694767155120705706,
        16641139526367750375,
    ];
}

#[derive(Eq, PartialEq)]
pub(crate) enum Result {
    Prime,
    Pseudoprime,
    Composite(u64),
}

impl Result {
    pub(crate) fn is_prime(&self) -> bool {
        *self == Result::Prime
    }
}

// Deterministic Miller-Rabin primality-checking algorithm, adapted to extract
// (some) dividers; it will fail to factor strong pseudoprimes.
#[allow(clippy::many_single_char_names)]
pub(crate) fn test<A: Arithmetic + Basis>(m: A) -> Result {
    use self::Result::*;

    let n = m.modulus();
    debug_assert!(n > 1);
    debug_assert!(n % 2 != 0);

    // n-1 = r 2ⁱ
    let i = (n - 1).trailing_zeros();
    let r = (n - 1) >> i;

    let one = m.one();
    let minus_one = m.minus_one();

    for _a in A::BASIS.iter() {
        let _a = _a % n;
        if _a == 0 {
            break;
        }

        let a = m.from_u64(_a);

        // x = a^r mod n
        let mut x = m.pow(a, r);

        {
            // y = ((x²)²...)² i times = x ^ (2ⁱ) = a ^ (r 2ⁱ) = x ^ (n - 1)
            let mut y = x;
            for _ in 0..i {
                y = m.mul(y, y)
            }
            if y != one {
                return Pseudoprime;
            };
        }

        if x == one || x == minus_one {
            continue;
        }

        loop {
            let y = m.mul(x, x);
            if y == one {
                return Composite(gcd(m.to_u64(x) - 1, m.modulus()));
            }
            if y == minus_one {
                // This basis element is not a witness of `n` being composite.
                // Keep looking.
                break;
            }
            x = y;
        }
    }

    Prime
}

// Used by build.rs' tests and debug assertions
#[allow(dead_code)]
pub(crate) fn is_prime(n: u64) -> bool {
    if n < 2 {
        false
    } else if n % 2 == 0 {
        n == 2
    } else {
        test::<Montgomery<u64>>(Montgomery::new(n)).is_prime()
    }
}

#[cfg(test)]
mod tests {
    use super::is_prime;
    use quickcheck::quickcheck;
    const LARGEST_U64_PRIME: u64 = 0xFFFFFFFFFFFFFFC5;

    fn primes() -> impl Iterator<Item = u64> {
        use crate::table::{NEXT_PRIME, P_INVS_U64};
        use std::iter::once;
        once(2)
            .chain(P_INVS_U64.iter().map(|(p, _, _)| *p))
            .chain(once(NEXT_PRIME))
    }

    #[test]
    fn largest_prime() {
        assert!(is_prime(LARGEST_U64_PRIME));
    }

    #[test]
    fn largest_composites() {
        for i in LARGEST_U64_PRIME + 1..=u64::MAX {
            assert!(!is_prime(i), "2⁶⁴ - {} reported prime", u64::MAX - i + 1);
        }
    }

    #[test]
    fn first_primes() {
        for p in primes() {
            assert!(is_prime(p), "{} reported composite", p);
        }
    }

    #[test]
    fn first_composites() {
        assert!(!is_prime(0));
        assert!(!is_prime(1));

        for (p, q) in primes().zip(primes().skip(1)) {
            for i in p + 1..q {
                assert!(!is_prime(i), "{} reported prime", i);
            }
        }
    }

    #[test]
    fn issue_1556() {
        // 10 425 511 = 2441 × 4271
        assert!(!is_prime(10_425_511));
    }

    #[test]
    fn small_semiprimes() {
        for p in primes() {
            for q in primes().take_while(|q| *q <= p) {
                let n = p * q;
                assert!(!is_prime(n), "{} = {} × {} reported prime", n, p, q);
            }
        }
    }

    quickcheck! {
        fn composites(i: u64, j: u64) -> bool {
            i < 2 || j < 2 || !is_prime(i*j)
        }
    }
}
<commit_msg>factor::miller_rabin: Generalise tests to 32 and 64b Montgomery<commit_after>// spell-checker:ignore (URL) appspot

use crate::numeric::*;

pub(crate) trait Basis {
    const BASIS: &'static [u64];
}

impl Basis for Montgomery<u64> {
    // Small set of bases for the Miller-Rabin prime test, valid for all 64b integers;
    //  discovered by Jim Sinclair on 2011-04-20, see miller-rabin.appspot.com
    #[allow(clippy::unreadable_literal)]
    const BASIS: &'static [u64] = &[2, 325, 9375, 28178, 450775, 9780504, 1795265022];
}

impl Basis for Montgomery<u32> {
    // Small set of bases for the Miller-Rabin prime test, valid for all 32b integers;
    //  discovered by Steve Worley on 2013-05-27, see miller-rabin.appspot.com
    #[allow(clippy::unreadable_literal)]
    const BASIS: &'static [u64] = &[
        4230279247111683200,
        14694767155120705706,
        16641139526367750375,
    ];
}

#[derive(Eq, PartialEq)]
pub(crate) enum Result {
    Prime,
    Pseudoprime,
    Composite(u64),
}

impl Result {
    pub(crate) fn is_prime(&self) -> bool {
        *self == Result::Prime
    }
}

// Deterministic Miller-Rabin primality-checking algorithm, adapted to extract
// (some) dividers; it will fail to factor strong pseudoprimes.
#[allow(clippy::many_single_char_names)]
pub(crate) fn test<A: Arithmetic + Basis>(m: A) -> Result {
    use self::Result::*;

    let n = m.modulus();
    debug_assert!(n > 1);
    debug_assert!(n % 2 != 0);

    // n-1 = r 2ⁱ
    let i = (n - 1).trailing_zeros();
    let r = (n - 1) >> i;

    let one = m.one();
    let minus_one = m.minus_one();

    for _a in A::BASIS.iter() {
        let _a = _a % n;
        if _a == 0 {
            break;
        }

        let a = m.from_u64(_a);

        // x = a^r mod n
        let mut x = m.pow(a, r);

        {
            // y = ((x²)²...)² i times = x ^ (2ⁱ) = a ^ (r 2ⁱ) = x ^ (n - 1)
            let mut y = x;
            for _ in 0..i {
                y = m.mul(y, y)
            }
            if y != one {
                return Pseudoprime;
            };
        }

        if x == one || x == minus_one {
            continue;
        }

        loop {
            let y = m.mul(x, x);
            if y == one {
                return Composite(gcd(m.to_u64(x) - 1, m.modulus()));
            }
            if y == minus_one {
                // This basis element is not a witness of `n` being composite.
                // Keep looking.
                break;
            }
            x = y;
        }
    }

    Prime
}

// Used by build.rs' tests and debug assertions
#[allow(dead_code)]
pub(crate) fn is_prime(n: u64) -> bool {
    if n < 2 {
        false
    } else if n % 2 == 0 {
        n == 2
    } else {
        test::<Montgomery<u64>>(Montgomery::new(n)).is_prime()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::numeric::{Arithmetic, Montgomery};
    use quickcheck::quickcheck;
    use std::iter;
    const LARGEST_U64_PRIME: u64 = 0xFFFFFFFFFFFFFFC5;

    fn primes() -> impl Iterator<Item = u64> {
        iter::once(2).chain(odd_primes())
    }

    fn odd_primes() -> impl Iterator<Item = u64> {
        use crate::table::{NEXT_PRIME, P_INVS_U64};
        P_INVS_U64
            .iter()
            .map(|(p, _, _)| *p)
            .chain(iter::once(NEXT_PRIME))
    }

    #[test]
    fn largest_prime() {
        assert!(is_prime(LARGEST_U64_PRIME));
    }

    #[test]
    fn largest_composites() {
        for i in LARGEST_U64_PRIME + 1..=u64::MAX {
            assert!(!is_prime(i), "2⁶⁴ - {} reported prime", u64::MAX - i + 1);
        }
    }

    #[test]
    fn two() {
        assert!(is_prime(2));
    }

    fn first_primes<A: Arithmetic + Basis>() {
        for p in odd_primes() {
            assert!(test(A::new(p)).is_prime(), "{} reported composite", p);
        }
    }

    #[test]
    fn first_primes_32() {
        first_primes::<Montgomery<u32>>()
    }

    #[test]
    fn first_primes_64() {
        first_primes::<Montgomery<u64>>()
    }

    #[test]
    fn one() {
        assert!(!is_prime(1));
    }
    #[test]
    fn zero() {
        assert!(!is_prime(0));
    }

    fn first_composites<A: Arithmetic + Basis>() {
        for (p, q) in primes().zip(odd_primes()) {
            for i in p + 1..q {
                assert!(!is_prime(i), "{} reported prime", i);
            }
        }
    }

    #[test]
    fn first_composites_32() {
        first_composites::<Montgomery<u32>>()
    }

    #[test]
    fn first_composites_64() {
        first_composites::<Montgomery<u64>>()
    }

    #[test]
    fn issue_1556() {
        // 10 425 511 = 2441 × 4271
        assert!(!is_prime(10_425_511));
    }

    fn small_semiprimes<A: Arithmetic + Basis>() {
        for p in odd_primes() {
            for q in odd_primes().take_while(|q| *q <= p) {
                let n = p * q;
                let m = A::new(n);
                assert!(!test(m).is_prime(), "{} = {} × {} reported prime", n, p, q);
            }
        }
    }

    #[test]
    fn small_semiprimes_32() {
        small_semiprimes::<Montgomery<u32>>()
    }

    #[test]
    fn small_semiprimes_64() {
        small_semiprimes::<Montgomery<u64>>()
    }

    quickcheck! {
        fn composites(i: u64, j: u64) -> bool {
            i < 2 || j < 2 || !is_prime(i*j)
        }
    }
}
<|endoftext|>


<commit_before>
#![feature(phase)]

#[phase(plugin, link)]
extern crate conrod;
extern crate graphics;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;

use sdl2_game_window::GameWindowSDL2;
use opengl_graphics::Gl;
use piston::{
    Game,
    GameEvent,
    GameWindowSettings,
    GameIterator,
    GameIteratorSettings,
    RenderArgs,
    Render,
};
use conrod::{
    button,
    label,
    number_dialer,
    slider,
    toggle,
    widget_matrix,
    Color,
    Point,
    Frame,
    UIContext,
};
use conrod::label::{
    Label,
    NoLabel,
};
use graphics::{
    Context,
    AddColor,
    Draw,
};

fn main() {

    // Create a SDL2 window.
    let mut window = GameWindowSDL2::new(
        GameWindowSettings {
            title: "Hello Conrod".to_string(),
            size: [600, 600],
            fullscreen: false,
            exit_on_esc: true
        }
    );

    // Some settings for how the game should be run.
    let game_iter_settings = GameIteratorSettings {
        updates_per_second: 180,
        max_frames_per_second: 60
    };

    // Create GameIterator to begin the event iteration loop.
    let mut game_iter = GameIterator::new(&mut window, &game_iter_settings);
    // Create OpenGL instance.
    let mut gl = Gl::new();
    // Create the UIContext and specify the name of a font that's in our "assets" directory.
    let mut uic = UIContext::new("Dense-Regular.otf");

    // TODO: Put the following vars all in an 'app' struct or something and pass
    // as a mut reference to struct instead.

    // Background color (for demonstration of button and sliders).
    let mut bg_color = Color::new(0.2, 0.35, 0.45, 1.0);
    // Should the button be shown (for demonstration of button).
    let mut show_button = false;
    // The label that will be drawn to the Toggle.
    let mut toggle_label = "OFF".to_string();
    // The number of pixels between the left side of the window and the title.
    let mut title_padding = 50f64;
    // The height of the vertical sliders (we will play with this using a number_dialer).
    let mut v_slider_height = 185f64;
    // The widget frame width (we'll use this to demo Framing and number_dialer).
    let mut frame_width = 1f64;
    // Bool matrix for widget_matrix demonstration.
    let mut bool_matrix = vec![
                            vec![true, true, true, true, true, true, true, true],
                            vec![true, false, false, false, false, true, true, true],
                            vec![true, false, true, true, false, true, false, true],
                            vec![true, false, false, false, false, true, true, true],
                            vec![true, false, true, true, true, true, false, true],
                            vec![true, false, true, true, true, true, false, true],
                            vec![true, false, true, true, true, true, false, true],
                            vec![true, true, true, true, true, true, true, true],
                            ];

    // Main program loop begins.
    loop {
        match game_iter.next() {
            None => break,
            Some(mut e) => handle_event(&mut e,
                                        &mut gl,
                                        &mut uic,
                                        &mut bg_color,
                                        &mut show_button,
                                        &mut toggle_label,
                                        &mut title_padding,
                                        &mut v_slider_height,
                                        &mut frame_width,
                                        &mut bool_matrix),
        }
    }

}

/// Match the game event.
fn handle_event(event: &mut GameEvent,
                gl: &mut Gl,
                uic: &mut UIContext,
                bg_color: &mut Color,
                show_button: &mut bool,
                toggle_label: &mut String,
                title_padding: &mut f64,
                v_slider_height: &mut f64,
                frame_width: &mut f64,
                bool_matrix: &mut Vec<Vec<bool>>) {
    uic.event(event);
    match *event {
        Render(ref mut args) => {
            draw_background(args, gl, bg_color);
            draw_ui(args,
                    gl,
                    uic,
                    bg_color,
                    show_button,
                    toggle_label,
                    title_padding,
                    v_slider_height,
                    frame_width,
                    bool_matrix);
        },
        _ => (),
    }
}

/// Draw the window background.
fn draw_background(args: &RenderArgs,
                   gl: &mut Gl,
                   bg_color: &mut Color) {
    // Set up a context to draw into.
    let context = &Context::abs(args.width as f64, args.height as f64);
    // Return the individual  elements of the background color.
    let (r, g, b, a) = bg_color.as_tuple();
    // Draw the background.
    context.rgba(r, g, b, a).draw(gl);
}

/// Draw the User Interface.
fn draw_ui(args: &RenderArgs,
           gl: &mut Gl,
           uic: &mut UIContext,
           bg_color: &mut Color,
           show_button: &mut bool,
           toggle_label: &mut String,
           title_padding: &mut f64,
           v_slider_height: &mut f64,
           frame_width: &mut f64,
           bool_matrix: &mut Vec<Vec<bool>>) {

    // Label example.
    label::draw(args, // RenderArgs.
                gl, // Open GL instance.
                uic, // UIContext.
                Point::new(*title_padding, 30f64, 0f64), // Screen position.
                48u32, // Font size.
                bg_color.plain_contrast(),
                "Widgets Demonstration");

    if *show_button {
        // Button widget example.
        button::draw(args, // RenderArgs.
                     gl, // Open GL instance.
                     uic, // UIContext.
                     0u64, // UI ID.
                     Point::new(50f64, 115f64, 0f64), // Screen position.
                     90f64, // Width.
                     60f64, // Height.
                     Frame(*frame_width, Color::black()), // Widget Frame.
                     Color::new(0.4f32, 0.75f32, 0.6f32, 1f32), // Button Color.
                     Label("PRESS", 24u32, Color::black()), // Label for button.
                     || { // Button "callback" event.
            *bg_color = Color::random();
        });
    }

    // Horizontal slider example.
    else {

        // Create the label for the slider.
        let pad = *title_padding as i16;
        let pad_string = pad.to_string();
        let label = "Padding: ".to_string().append(pad_string.as_slice());

        // Draw the slider.
        slider::draw(args, // RenderArgs.
                     gl, // OpenGL Instance.
                     uic, // UIContext.
                     1u64, // UIID
                     Point::new(50.0f64, 115.0, 0.0), // Screen position.
                     200f64, // Width.
                     50f64, // Height.
                     Frame(*frame_width, Color::black()), // Widget Frame.
                     Color::new(0.5, 0.3, 0.6, 1.0), // Rectangle color.
                     //NoLabel,
                     Label(label.as_slice(), 24u32, Color::white()),
                     pad as i16, // Slider value.
                     10i16, // Min value.
                     250i16, // Max value.
                     |new_pad| {
            *title_padding = new_pad as f64;
        });

    }

    // Clone the label toggle to be drawn.
    let label = toggle_label.clone();

    // Toggle widget example.
    toggle::draw(args, // RenderArgs.
                 gl, // Open GL instance.
                 uic, // UIContext.
                 2u64, // UI ID.
                 Point::new(50f64, 200f64, 0f64), // Screen position.
                 75f64, // Width.
                 75f64, // Height.
                 Frame(*frame_width, Color::black()), // Widget Frame.
                 Color::new(0.6f32, 0.25f32, 0.75f32, 1f32), // Button Color.
                 Label(label.as_slice(), 24u32, Color::white()),
                 *show_button, // bool.
                 |value| {
        *show_button = value;
        match value {
            true => {
                *toggle_label = "ON".to_string();
            },
            false => {
                *toggle_label = "OFF".to_string();
            },
        }
    });

    // Let's draw a slider for each color element.
    // 0 => red, 1 => green, 2 => blue.
    for i in range(0u, 3) {

        // We'll color the slider similarly to the color element which it will control.
        let color = match i {
            0u => Color::new(0.75f32, 0.3f32, 0.3f32, 1f32),
            1u => Color::new(0.3f32, 0.75f32, 0.3f32, 1f32),
            _  => Color::new(0.3f32, 0.3f32, 0.75f32, 1f32),
        };

        // Grab the value of the color element.
        let value = match i {
            0u => bg_color.r(),
            1u => bg_color.g(),
            _  => bg_color.b(),
        };

        // Create the label to be drawn with the slider.
        let mut label = value.to_string();
        if label.len() > 4u { label.truncate(4u); }

        // Vertical slider widget example.
        slider::draw(args, // RenderArgs.
                     gl, // Open GL instance
                     uic, // UIContext.
                     3u64 + i as u64, // UI ID.
                     Point::new(50f64 + i as f64 * 60f64, 300f64, 0f64), // Position.
                     35f64, // Width.
                     *v_slider_height, // Height.
                     Frame(*frame_width, Color::black()), // Widget Frame.
                     color, // Slider color.
                     //NoLabel,
                     Label(label.as_slice(), 24u32, Color::white()),
                     value, // Slider value.
                     0f32, // Minimum value.
                     1f32, // Maximum value.
                     |color| {
            match i {
                0u => { bg_color.set_r(color); },
                1u => { bg_color.set_g(color); },
                _ => { bg_color.set_b(color); },
            }
        });

    }

    // Number Dialer example.
    number_dialer::draw(args, // RenderArgs.
                        gl, // OpenGL instance.
                        uic, // UIContext.
                        6u64, // UIID.
                        Point::new(350.0, 115.0, 0.0), // Position.
                        24u32, // Number Dialer font size.
                        Frame(*frame_width, Color::black()), // Widget Frame
                        bg_color.invert(), // Number Dialer Color.
                        Label("Height (pixels)", 24u32, bg_color.invert().plain_contrast()),
                        *v_slider_height, // Initial value.
                        25f64, // Minimum value.
                        250f64, // Maximum value.
                        1u8, // Precision (number of digits to show after decimal point).
                        |new_height| { // Callback closure.
        *v_slider_height = new_height;
    });

    // Number Dialer example.
    number_dialer::draw(args, // RenderArgs.
                        gl, // OpenGL instance.
                        uic, // UIContext.
                        7u64, // UIID.
                        Point::new(350.0, 195.0, 0.0), // Position.
                        24u32, // Number Dialer font size.
                        Frame(4.0, bg_color.plain_contrast()), // Widget Frame
                        bg_color.invert().plain_contrast(), // Number Dialer Color.
                        Label("Frame (pixels)", 24u32, bg_color.plain_contrast()),
                        *frame_width, // Initial value.
                        0f64, // Minimum value.
                        15f64, // Maximum value.
                        2u8, // Precision (number of digits to show after decimal point).
                        |new_width| { // Callback closure.
        *frame_width = new_width;
    });

    // A demonstration using widget_matrix to easily draw
    // a matrix of any kind of widget.
    let (cols, rows) = (8u, 8u);
    widget_matrix::draw(cols, // cols.
                        rows, // rows.
                        Point::new(300.0, 270.0, 0.0), // matrix position.
                        260.0, // width.
                        260.0, // height.
                        |num, col, row, pos, width, height| {
        // Now draw the widgets with the given callback.
        let val = (*bool_matrix)[col][row];
        toggle::draw(args, gl, uic, 8u64 + num as u64, pos, width, height,
                     Frame(*frame_width, Color::black()),
                     Color::new(0.5 + (col as f32 / cols as f32) / 2.0,
                                0.75,
                                1.0 - (row as f32 / rows as f32) / 2.0,
                                1.0),
                     NoLabel,
                     val,
                     |new_val| {
            *bool_matrix.get_mut(col).get_mut(row) = new_val;
        });
    });

}

<commit_msg>Fixed mistake in defaults<commit_after>
#![feature(phase)]

#[phase(plugin, link)]
extern crate conrod;
extern crate graphics;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;

use sdl2_game_window::GameWindowSDL2;
use opengl_graphics::Gl;
use piston::{
    Game,
    GameEvent,
    GameWindowSettings,
    GameIterator,
    GameIteratorSettings,
    RenderArgs,
    Render,
};
use conrod::{
    button,
    label,
    number_dialer,
    slider,
    toggle,
    widget_matrix,
    Color,
    Point,
    Frame,
    UIContext,
};
use conrod::label::{
    Label,
    NoLabel,
};
use graphics::{
    Context,
    AddColor,
    Draw,
};

fn main() {

    // Create a SDL2 window.
    let mut window = GameWindowSDL2::new(
        GameWindowSettings {
            title: "Hello Conrod".to_string(),
            size: [600, 600],
            fullscreen: false,
            exit_on_esc: true
        }
    );

    // Some settings for how the game should be run.
    let game_iter_settings = GameIteratorSettings {
        updates_per_second: 180,
        max_frames_per_second: 60
    };

    // Create GameIterator to begin the event iteration loop.
    let mut game_iter = GameIterator::new(&mut window, &game_iter_settings);
    // Create OpenGL instance.
    let mut gl = Gl::new();
    // Create the UIContext and specify the name of a font that's in our "assets" directory.
    let mut uic = UIContext::new("Dense-Regular.otf");

    // TODO: Put the following vars all in an 'app' struct or something and pass
    // as a mut reference to struct instead.

    // Background color (for demonstration of button and sliders).
    let mut bg_color = Color::new(0.2, 0.35, 0.45, 1.0);
    // Should the button be shown (for demonstration of button).
    let mut show_button = false;
    // The label that will be drawn to the Toggle.
    let mut toggle_label = "OFF".to_string();
    // The number of pixels between the left side of the window and the title.
    let mut title_padding = 50f64;
    // The height of the vertical sliders (we will play with this using a number_dialer).
    let mut v_slider_height = 185f64;
    // The widget frame width (we'll use this to demo Framing and number_dialer).
    let mut frame_width = 1f64;
    // Bool matrix for widget_matrix demonstration.
    let mut bool_matrix = vec![
                            vec![true, true, true, true, true, true, true, true],
                            vec![true, false, false, false, false, false, false, true],
                            vec![true, false, true, false, true, true, true, true],
                            vec![true, false, true, false, true, true, true, true],
                            vec![true, false, false, false, true, true, true, true],
                            vec![true, true, true, true, true, true, true, true],
                            vec![true, true, false, true, false, false, false, true],
                            vec![true, true, true, true, true, true, true, true],
                            ];

    // Main program loop begins.
    loop {
        match game_iter.next() {
            None => break,
            Some(mut e) => handle_event(&mut e,
                                        &mut gl,
                                        &mut uic,
                                        &mut bg_color,
                                        &mut show_button,
                                        &mut toggle_label,
                                        &mut title_padding,
                                        &mut v_slider_height,
                                        &mut frame_width,
                                        &mut bool_matrix),
        }
    }

}

/// Match the game event.
fn handle_event(event: &mut GameEvent,
                gl: &mut Gl,
                uic: &mut UIContext,
                bg_color: &mut Color,
                show_button: &mut bool,
                toggle_label: &mut String,
                title_padding: &mut f64,
                v_slider_height: &mut f64,
                frame_width: &mut f64,
                bool_matrix: &mut Vec<Vec<bool>>) {
    uic.event(event);
    match *event {
        Render(ref mut args) => {
            draw_background(args, gl, bg_color);
            draw_ui(args,
                    gl,
                    uic,
                    bg_color,
                    show_button,
                    toggle_label,
                    title_padding,
                    v_slider_height,
                    frame_width,
                    bool_matrix);
        },
        _ => (),
    }
}

/// Draw the window background.
fn draw_background(args: &RenderArgs,
                   gl: &mut Gl,
                   bg_color: &mut Color) {
    // Set up a context to draw into.
    let context = &Context::abs(args.width as f64, args.height as f64);
    // Return the individual  elements of the background color.
    let (r, g, b, a) = bg_color.as_tuple();
    // Draw the background.
    context.rgba(r, g, b, a).draw(gl);
}

/// Draw the User Interface.
fn draw_ui(args: &RenderArgs,
           gl: &mut Gl,
           uic: &mut UIContext,
           bg_color: &mut Color,
           show_button: &mut bool,
           toggle_label: &mut String,
           title_padding: &mut f64,
           v_slider_height: &mut f64,
           frame_width: &mut f64,
           bool_matrix: &mut Vec<Vec<bool>>) {

    // Label example.
    label::draw(args, // RenderArgs.
                gl, // Open GL instance.
                uic, // UIContext.
                Point::new(*title_padding, 30f64, 0f64), // Screen position.
                48u32, // Font size.
                bg_color.plain_contrast(),
                "Widgets Demonstration");

    if *show_button {
        // Button widget example.
        button::draw(args, // RenderArgs.
                     gl, // Open GL instance.
                     uic, // UIContext.
                     0u64, // UI ID.
                     Point::new(50f64, 115f64, 0f64), // Screen position.
                     90f64, // Width.
                     60f64, // Height.
                     Frame(*frame_width, Color::black()), // Widget Frame.
                     Color::new(0.4f32, 0.75f32, 0.6f32, 1f32), // Button Color.
                     Label("PRESS", 24u32, Color::black()), // Label for button.
                     || { // Button "callback" event.
            *bg_color = Color::random();
        });
    }

    // Horizontal slider example.
    else {

        // Create the label for the slider.
        let pad = *title_padding as i16;
        let pad_string = pad.to_string();
        let label = "Padding: ".to_string().append(pad_string.as_slice());

        // Draw the slider.
        slider::draw(args, // RenderArgs.
                     gl, // OpenGL Instance.
                     uic, // UIContext.
                     1u64, // UIID
                     Point::new(50.0f64, 115.0, 0.0), // Screen position.
                     200f64, // Width.
                     50f64, // Height.
                     Frame(*frame_width, Color::black()), // Widget Frame.
                     Color::new(0.5, 0.3, 0.6, 1.0), // Rectangle color.
                     //NoLabel,
                     Label(label.as_slice(), 24u32, Color::white()),
                     pad as i16, // Slider value.
                     10i16, // Min value.
                     250i16, // Max value.
                     |new_pad| {
            *title_padding = new_pad as f64;
        });

    }

    // Clone the label toggle to be drawn.
    let label = toggle_label.clone();

    // Toggle widget example.
    toggle::draw(args, // RenderArgs.
                 gl, // Open GL instance.
                 uic, // UIContext.
                 2u64, // UI ID.
                 Point::new(50f64, 200f64, 0f64), // Screen position.
                 75f64, // Width.
                 75f64, // Height.
                 Frame(*frame_width, Color::black()), // Widget Frame.
                 Color::new(0.6f32, 0.25f32, 0.75f32, 1f32), // Button Color.
                 Label(label.as_slice(), 24u32, Color::white()),
                 *show_button, // bool.
                 |value| {
        *show_button = value;
        match value {
            true => {
                *toggle_label = "ON".to_string();
            },
            false => {
                *toggle_label = "OFF".to_string();
            },
        }
    });

    // Let's draw a slider for each color element.
    // 0 => red, 1 => green, 2 => blue.
    for i in range(0u, 3) {

        // We'll color the slider similarly to the color element which it will control.
        let color = match i {
            0u => Color::new(0.75f32, 0.3f32, 0.3f32, 1f32),
            1u => Color::new(0.3f32, 0.75f32, 0.3f32, 1f32),
            _  => Color::new(0.3f32, 0.3f32, 0.75f32, 1f32),
        };

        // Grab the value of the color element.
        let value = match i {
            0u => bg_color.r(),
            1u => bg_color.g(),
            _  => bg_color.b(),
        };

        // Create the label to be drawn with the slider.
        let mut label = value.to_string();
        if label.len() > 4u { label.truncate(4u); }

        // Vertical slider widget example.
        slider::draw(args, // RenderArgs.
                     gl, // Open GL instance
                     uic, // UIContext.
                     3u64 + i as u64, // UI ID.
                     Point::new(50f64 + i as f64 * 60f64, 300f64, 0f64), // Position.
                     35f64, // Width.
                     *v_slider_height, // Height.
                     Frame(*frame_width, Color::black()), // Widget Frame.
                     color, // Slider color.
                     //NoLabel,
                     Label(label.as_slice(), 24u32, Color::white()),
                     value, // Slider value.
                     0f32, // Minimum value.
                     1f32, // Maximum value.
                     |color| {
            match i {
                0u => { bg_color.set_r(color); },
                1u => { bg_color.set_g(color); },
                _ => { bg_color.set_b(color); },
            }
        });

    }

    // Number Dialer example.
    number_dialer::draw(args, // RenderArgs.
                        gl, // OpenGL instance.
                        uic, // UIContext.
                        6u64, // UIID.
                        Point::new(350.0, 115.0, 0.0), // Position.
                        24u32, // Number Dialer font size.
                        Frame(*frame_width, Color::black()), // Widget Frame
                        bg_color.invert(), // Number Dialer Color.
                        Label("Height (pixels)", 24u32, bg_color.invert().plain_contrast()),
                        *v_slider_height, // Initial value.
                        25f64, // Minimum value.
                        250f64, // Maximum value.
                        1u8, // Precision (number of digits to show after decimal point).
                        |new_height| { // Callback closure.
        *v_slider_height = new_height;
    });

    // Number Dialer example.
    number_dialer::draw(args, // RenderArgs.
                        gl, // OpenGL instance.
                        uic, // UIContext.
                        7u64, // UIID.
                        Point::new(350.0, 195.0, 0.0), // Position.
                        24u32, // Number Dialer font size.
                        Frame(4.0, bg_color.plain_contrast()), // Widget Frame
                        bg_color.invert().plain_contrast(), // Number Dialer Color.
                        Label("Frame (pixels)", 24u32, bg_color.plain_contrast()),
                        *frame_width, // Initial value.
                        0f64, // Minimum value.
                        15f64, // Maximum value.
                        2u8, // Precision (number of digits to show after decimal point).
                        |new_width| { // Callback closure.
        *frame_width = new_width;
    });

    // A demonstration using widget_matrix to easily draw
    // a matrix of any kind of widget.
    let (cols, rows) = (8u, 8u);
    widget_matrix::draw(cols, // cols.
                        rows, // rows.
                        Point::new(300.0, 270.0, 0.0), // matrix position.
                        260.0, // width.
                        260.0, // height.
                        |num, col, row, pos, width, height| {
        // Now draw the widgets with the given callback.
        let val = (*bool_matrix)[col][row];
        toggle::draw(args, gl, uic, 8u64 + num as u64, pos, width, height,
                     Frame(*frame_width, Color::black()),
                     Color::new(0.5 + (col as f32 / cols as f32) / 2.0,
                                0.75,
                                1.0 - (row as f32 / rows as f32) / 2.0,
                                1.0),
                     NoLabel,
                     val,
                     |new_val| {
            *bool_matrix.get_mut(col).get_mut(row) = new_val;
        });
    });

}

<|endoftext|>


<commit_before>// use to create a .so/.dll
#![crate_type = "dylib"]

// we use crate collections which contains only
// collections that does not need the runtime to run by default
extern crate collections;
extern crate native;
extern crate libc;

// NOTE: at first I wanted to push the example further and not to
// include the std crate, which is basically what you would want to
// do for system programming
//
// but too much features will not be available without implemenenting
// your own memory allocator
// i.e  "".to_string  or insertion in collection etc.
//
// waiting to write a dedicated example about building without std
// here is basically what you need to append at top of your file
//
//#![no_std]
//#![crate_type = "dylib"]
//#![feature(phase)]
//
//#[phase(plugin,link)]
//extern crate core;

use libc::types::common::c95::c_void;
use std::mem::transmute;
// we need to use TreeMap, and not HashMap
// because HashMap use a RNG running in a local thread
// that is init by rust runtime

// note that I could use HashMap with SipHash and own seeded RNG
use collections::TreeMap;

#[no_mangle]
pub extern fn create_hash() -> *mut c_void {
    let mut english_to_french =  box TreeMap::new();
    english_to_french.insert("one".to_string(), "un".to_string());
    english_to_french.insert("two".to_string(), "deux".to_string());

    return unsafe {transmute(english_to_french)};
}

#[no_mangle]
pub extern fn print_hash(english_to_french_ptr: *mut c_void) {
    let english_to_french : Box<TreeMap<String, String>> = unsafe {
        transmute(english_to_french_ptr)
    };
    for (english, french) in english_to_french.iter() {
        println!(
            "in french '{}' is '{}'",
            *english,
            *french
        );
    }

}
<commit_msg>remove transmute from treemap example<commit_after>// use to create a .so/.dll
#![crate_type = "dylib"]

// we use crate collections which contains only
// collections that does not need the runtime to run by default
extern crate collections;
extern crate native;
extern crate libc;

// NOTE: at first I wanted to push the example further and not to
// include the std crate, which is basically what you would want to
// do for system programming
//
// but too much features will not be available without implemenenting
// your own memory allocator
// i.e  "".to_string  or insertion in collection etc.
//
// waiting to write a dedicated example about building without std
// here is basically what you need to append at top of your file
//
//#![no_std]
//#![crate_type = "dylib"]
//#![feature(phase)]
//
//#[phase(plugin,link)]
//extern crate core;

// we need to use TreeMap, and not HashMap
// because HashMap use a RNG running in a local thread
// that is init by rust runtime

// note that I could use HashMap with SipHash and own seeded RNG
use collections::TreeMap;

#[no_mangle]
pub extern fn create_hash() -> Box<TreeMap<String,String>> {
    let mut english_to_french =  box TreeMap::new();
    english_to_french.insert("one".to_string(), "un".to_string());
    english_to_french.insert("two".to_string(), "deux".to_string());

    english_to_french
}

#[no_mangle]
pub extern fn print_hash(english_to_french: Box<TreeMap<String,String>>) {
    for (english, french) in english_to_french.iter() {
        println!(
            "in french '{}' is '{}'",
            *english,
            *french
        );
    }

}
<|endoftext|>


<commit_before>//! Contributed by Gavin Baker <gavinb@antonym.org>
//! Adapted from the Go version

use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
use std::iter::repeat;

/// Simple 8-bit grayscale image
struct ImageGray8 {
    width: usize,
    height: usize,
    data: Vec<u8>,
}

fn load_pgm(filename: &str) -> ImageGray8 {
    // Open file
    let mut file = BufReader::new(File::open(filename).unwrap());

    // Read header
    let mut magic_in = String::new();
    let _ = file.read_line(&mut magic_in).unwrap();
    let mut width_in = String::new();
    let _ = file.read_line(&mut width_in).unwrap();
    let mut height_in = String::new();
    let _ = file.read_line(&mut height_in).unwrap();
    let mut maxval_in = String::new();
    let _ = file.read_line(&mut maxval_in).unwrap();

    assert_eq!(magic_in, "P5\n");
    assert_eq!(maxval_in, "255\n");

    // Parse header

    let width = width_in.trim().parse::<usize>().unwrap();
    let height: usize = height_in.trim().parse::<usize>().unwrap();

    println!("Reading pgm file {}: {} x {}", filename, width, height);

    // Create image and allocate buffer

    let mut img = ImageGray8 {
        width,
        height,
        data: repeat(0_u8).take(width * height).collect(),
    };

    // Read image data
    let len = img.data.len();
    match file.read(&mut img.data) {
        Ok(bytes_read) if bytes_read == len => println!("Read {} bytes", bytes_read),
        Ok(bytes_read) => println!("Error: read {} bytes, expected {}", bytes_read, len),
        Err(e) => println!("error reading: {}", e),
    }

    img
}

fn save_pgm(img: &ImageGray8, filename: &str) {
    // Open file
    let mut file = BufWriter::new(File::create(filename).unwrap());

    // Write header
    if let Err(e) = writeln!(&mut file, "P5\n{}\n{}\n255", img.width, img.height) {
        println!("Failed to write header: {}", e);
    }

    println!(
        "Writing pgm file {}: {} x {}",
        filename, img.width, img.height
    );

    // Write binary image data
    if let Err(e) = file.write_all(&(img.data[..])) {
        println!("Failed to image data: {}", e);
    }
}

fn hough(image: &ImageGray8, out_width: usize, out_height: usize) -> ImageGray8 {
    let in_width = image.width;
    let in_height = image.height;

    // Allocate accumulation buffer
    let out_height = ((out_height / 2) * 2) as usize;
    let mut accum = ImageGray8 {
        width: out_width,
        height: out_height,
        data: repeat(255).take(out_width * out_height).collect(),
    };

    // Transform extents
    let rmax = (in_width as f64).hypot(in_height as f64);
    let dr = rmax / (out_height / 2) as f64;
    let dth = std::f64::consts::PI / out_width as f64;

    // Process input image in raster order
    for y in 0..in_height {
        for x in 0..in_width {
            let in_idx = y * in_width + x;
            let col = image.data[in_idx];
            if col == 255 {
                continue;
            }

            // Project into rho,theta space
            for jtx in 0..out_width {
                let th = dth * (jtx as f64);
                let r = (x as f64) * (th.cos()) + (y as f64) * (th.sin());

                let iry = out_height / 2 - (r / (dr as f64) + 0.5).floor() as usize;
                let out_idx = jtx + iry * out_width;
                let col = accum.data[out_idx];
                if col > 0 {
                    accum.data[out_idx] = col - 1;
                }
            }
        }
    }
    accum
}

fn main() {
    let image = load_pgm("resources/Pentagon.pgm");

    let accum = hough(&image, 460, 360);

    save_pgm(&accum, "hough.pgm");
}
<commit_msg>Revert a conflicting change<commit_after>//! Contributed by Gavin Baker <gavinb@antonym.org>
//! Adapted from the Go version

use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
use std::iter::repeat;

/// Simple 8-bit grayscale image
struct ImageGray8 {
    width: usize,
    height: usize,
    data: Vec<u8>,
}

fn load_pgm(filename: &str) -> ImageGray8 {
    // Open file
    let mut file = BufReader::new(File::open(filename).unwrap());

    // Read header
    let mut magic_in = String::new();
    let _ = file.read_line(&mut magic_in).unwrap();
    let mut width_in = String::new();
    let _ = file.read_line(&mut width_in).unwrap();
    let mut height_in = String::new();
    let _ = file.read_line(&mut height_in).unwrap();
    let mut maxval_in = String::new();
    let _ = file.read_line(&mut maxval_in).unwrap();

    assert_eq!(magic_in, "P5\n");
    assert_eq!(maxval_in, "255\n");

    // Parse header

    let width = width_in.trim().parse::<usize>().unwrap();
    let height: usize = height_in.trim().parse::<usize>().unwrap();

    println!("Reading pgm file {}: {} x {}", filename, width, height);

    // Create image and allocate buffer

    let mut img = ImageGray8 {
        width,
        height,
        data: repeat(0u8).take(width * height).collect(),
    };

    // Read image data
    let len = img.data.len();
    match file.read(&mut img.data) {
        Ok(bytes_read) if bytes_read == len => println!("Read {} bytes", bytes_read),
        Ok(bytes_read) => println!("Error: read {} bytes, expected {}", bytes_read, len),
        Err(e) => println!("error reading: {}", e),
    }

    img
}

fn save_pgm(img: &ImageGray8, filename: &str) {
    // Open file
    let mut file = BufWriter::new(File::create(filename).unwrap());

    // Write header
    if let Err(e) = writeln!(&mut file, "P5\n{}\n{}\n255", img.width, img.height) {
        println!("Failed to write header: {}", e);
    }

    println!(
        "Writing pgm file {}: {} x {}",
        filename, img.width, img.height
    );

    // Write binary image data
    if let Err(e) = file.write_all(&(img.data[..])) {
        println!("Failed to image data: {}", e);
    }
}

fn hough(image: &ImageGray8, out_width: usize, out_height: usize) -> ImageGray8 {
    let in_width = image.width;
    let in_height = image.height;

    // Allocate accumulation buffer
    let out_height = ((out_height / 2) * 2) as usize;
    let mut accum = ImageGray8 {
        width: out_width,
        height: out_height,
        data: repeat(255).take(out_width * out_height).collect(),
    };

    // Transform extents
    let rmax = (in_width as f64).hypot(in_height as f64);
    let dr = rmax / (out_height / 2) as f64;
    let dth = std::f64::consts::PI / out_width as f64;

    // Process input image in raster order
    for y in 0..in_height {
        for x in 0..in_width {
            let in_idx = y * in_width + x;
            let col = image.data[in_idx];
            if col == 255 {
                continue;
            }

            // Project into rho,theta space
            for jtx in 0..out_width {
                let th = dth * (jtx as f64);
                let r = (x as f64) * (th.cos()) + (y as f64) * (th.sin());

                let iry = out_height / 2 - (r / (dr as f64) + 0.5).floor() as usize;
                let out_idx = jtx + iry * out_width;
                let col = accum.data[out_idx];
                if col > 0 {
                    accum.data[out_idx] = col - 1;
                }
            }
        }
    }
    accum
}

fn main() {
    let image = load_pgm("resources/Pentagon.pgm");

    let accum = hough(&image, 460, 360);

    save_pgm(&accum, "hough.pgm");
}
<|endoftext|>


<commit_before>#![feature(libc)]
#![feature(tcp)]

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io;
use std::env;
use std::mem;
use std::str::FromStr;
use std::io::Read;
use std::io::Write;

extern crate libc;

fn main() {
    let port_ : Option<String> = env::args().skip(2).next();
    if let None = port_ {
        println!("usage: {} -p port", env::args().next().unwrap());
        return;
    }

    let port = u16::from_str(&port_.unwrap());
    if let Err(ref e) = port {
        println!("invalid port number given: {}", e);
        return;
    }

    let listener = TcpListener::bind(("127.0.0.1", port.unwrap()));
    if let Err(ref e) = listener {
        println!("failed to listen on port: {}", e);
        return;
    }

    for stream in listener.unwrap().incoming() {
        match stream {
            Ok(stream) => {
                thread::spawn(move|| {
                    handle_client(stream)
                });
            }
            Err(e) => {
                println!("failed to accept connection: {}", e);
            }
        }
    }
}

fn handle_client(mut stream: TcpStream) {
    let mut challenge : u32 = 0;
    let buf : &mut [u8; 4] = unsafe{ mem::transmute(&mut challenge) };
    let mut nread;

    let _ = stream.set_nodelay(true);

    loop {
        nread = 0;
        while nread < buf.len() {
            match stream.read(&mut buf[nread..]) {
                Ok(n) if n == 0 => return,
                Ok(n) => nread += n,
                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
                Err(e) => {
                    println!("{}", e);
                    return;
                }
            }
        }

        challenge = u32::from_be(challenge);
        if challenge == 0 {
            unsafe { libc::exit(0 as libc::c_int); }
        }
        challenge = u32::to_be(challenge + 1);

        let mut nwritten = 0;
        while nwritten < buf.len() {
            match stream.write(&buf[nwritten..]) {
                Ok(n) => nwritten += n,
                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
                Err(e) => {
                    println!("{}", e);
                    return;
                }
            }
        }

        let _ = stream.flush();
    }
}
<commit_msg>remove libc dependency<commit_after>#![feature(tcp)]

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io;
use std::env;
use std::mem;
use std::str::FromStr;
use std::io::Read;
use std::io::Write;

fn main() {
    let port_ : Option<String> = env::args().skip(2).next();
    if let None = port_ {
        println!("usage: {} -p port", env::args().next().unwrap());
        return;
    }

    let port = u16::from_str(&port_.unwrap());
    if let Err(ref e) = port {
        println!("invalid port number given: {}", e);
        return;
    }

    let listener = TcpListener::bind(("127.0.0.1", port.unwrap()));
    if let Err(ref e) = listener {
        println!("failed to listen on port: {}", e);
        return;
    }

    for stream in listener.unwrap().incoming() {
        match stream {
            Ok(stream) => {
                thread::spawn(move|| {
                    handle_client(stream)
                });
            }
            Err(e) => {
                println!("failed to accept connection: {}", e);
            }
        }
    }
}

fn handle_client(mut stream: TcpStream) {
    let mut challenge : u32 = 0;
    let buf : &mut [u8; 4] = unsafe{ mem::transmute(&mut challenge) };
    let mut nread;

    let _ = stream.set_nodelay(true);

    loop {
        nread = 0;
        while nread < buf.len() {
            match stream.read(&mut buf[nread..]) {
                Ok(n) if n == 0 => return,
                Ok(n) => nread += n,
                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
                Err(e) => {
                    println!("{}", e);
                    return;
                }
            }
        }

        challenge = u32::from_be(challenge);
        if challenge == 0 {
            std::process::exit(0);
        }
        challenge = u32::to_be(challenge + 1);

        let mut nwritten = 0;
        while nwritten < buf.len() {
            match stream.write(&buf[nwritten..]) {
                Ok(n) => nwritten += n,
                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
                Err(e) => {
                    println!("{}", e);
                    return;
                }
            }
        }

        let _ = stream.flush();
    }
}
<|endoftext|>


<commit_before>use crate::remote::RemoteStatus;
use crate::tui::state::ProgressTime;
use crate::tui::widget_util::{block, text};
use crate::tui::{state::SharedState, widget_util::widget::WrapHelper};
use crate::tui::{state::StateEvent, UIState};
use crate::util;
use crate::{
    series::{LoadedSeries, Series},
    tui::component::Component,
};
use anime::remote::{ScoreParser, SeriesDate};
use chrono::Utc;
use smallvec::{smallvec, SmallVec};
use std::{
    borrow::Cow,
    sync::{atomic::Ordering, Arc},
    time::Duration,
};
use std::{fmt, sync::atomic::AtomicU32};
use tokio::{task, time};
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::Color;
use tui::terminal::Frame;
use tui::text::{Span, Text};
use tui::widgets::Paragraph;
use tui_utils::{
    grid_pos,
    widgets::{Fragment, OverflowMode, SpanOptions, TextFragments},
};
use util::ScopedTask;

pub struct InfoPanel {
    progress_remaining_secs: Arc<AtomicU32>,
    #[allow(dead_code)]
    event_monitor_task: ScopedTask<()>,
}

impl InfoPanel {
    pub fn new(state: &SharedState) -> Self {
        let progress_remaining_secs = Arc::new(AtomicU32::default());
        let event_monitor_task =
            Self::spawn_episode_event_monitor(state, Arc::clone(&progress_remaining_secs)).into();

        Self {
            progress_remaining_secs,
            event_monitor_task,
        }
    }

    fn spawn_episode_event_monitor(
        state: &SharedState,
        progress_remaining_secs: Arc<AtomicU32>,
    ) -> task::JoinHandle<()> {
        let state = state.clone();

        task::spawn(async move {
            let mut events = {
                let state = state.lock();
                state.events.subscribe()
            };

            #[allow(unused_variables)]
            let mut progress_task: Option<ScopedTask<_>> = None;

            while let Ok(event) = events.recv().await {
                #[allow(unused_assignments)]
                match event {
                    StateEvent::StartedEpisode(progress_time) => {
                        let state = state.clone();
                        let remaining_secs = Arc::clone(&progress_remaining_secs);

                        let task =
                            Self::spawn_progress_monitor_task(state, remaining_secs, progress_time)
                                .into();

                        progress_task = Some(task);
                    }
                    StateEvent::FinishedEpisode => {
                        progress_remaining_secs.store(0, Ordering::SeqCst);
                        progress_task = None;

                        let mut state = state.lock();
                        state.mark_dirty();
                    }
                }
            }
        })
    }

    fn spawn_progress_monitor_task(
        state: SharedState,
        remaining_secs: Arc<AtomicU32>,
        progress_time: ProgressTime,
    ) -> task::JoinHandle<()> {
        task::spawn(async move {
            let mut first_iter = true;

            loop {
                let cur_progress = remaining_secs.load(Ordering::SeqCst);

                if cur_progress == 0 && !first_iter {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                let diff = progress_time - Utc::now();
                let secs = diff.num_seconds();

                if secs <= 0 {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                remaining_secs.store(secs as u32, Ordering::SeqCst);
                first_iter = false;

                {
                    state.lock().mark_dirty();
                }

                time::sleep(Duration::from_secs(60)).await;
            }
        })
    }

    fn text_display_layout(rect: Rect) -> Vec<Rect> {
        Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(2), Constraint::Percentage(100)])
            .margin(2)
            .split(rect)
    }

    fn draw_text_panel<'a, B, T>(header: Span, body: T, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        T: Into<Text<'a>>,
    {
        let layout = Self::text_display_layout(rect);

        let header_widget = Paragraph::new(header).alignment(Alignment::Center);
        frame.render_widget(header_widget, layout[0]);

        let body_widget = Paragraph::new(body).alignment(Alignment::Center).wrapped();
        frame.render_widget(body_widget, layout[1]);
    }

    fn draw_no_users_info<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let body = vec![
            "Add an account by pressing 'u' to open".into(),
            "user management and then by pressing tab".into(),
            "to switch to the add user panel.".into(),
            "".into(),
            "Then open the auth URL in your browser".into(),
            "by pressing Ctrl + O, and follow its instructions.".into(),
            "Once you have a token, paste it in with either".into(),
            "Ctrl + Shift + V or Ctrl + V.".into(),
            "".into(),
            "More detailed instructions here:".into(),
            "https://github.com/Acizza/anup#adding-an-account".into(),
        ];

        Self::draw_text_panel(text::bold("No Accounts Added"), body, rect, frame);
    }

    fn draw_no_series_found<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let body = vec![
            "Add one by pressing the 'a' key.".into(),
            "".into(),
            "The opened panel will require you to specify".into(),
            "a name for the series you want to add.".into(),
            "".into(),
            "For automatic detection, the name should be".into(),
            "similar to the name of the folder the series".into(),
            "is in on disk.".into(),
        ];

        Self::draw_text_panel(text::bold("No Series Found"), body, rect, frame);
    }

    fn draw_series_error<B, E>(err: E, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        E: fmt::Display,
    {
        let header = text::bold_with("Error Loading Series", |s| s.fg(Color::Red));
        let body = text::with_color(err.to_string(), Color::Red);

        Self::draw_text_panel(header, body, rect, frame);
    }

    #[allow(clippy::too_many_lines)]
    fn draw_series_info<B>(
        &self,
        state: &UIState,
        series: &Series,
        rect: Rect,
        frame: &mut Frame<B>,
    ) where
        B: Backend,
    {
        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(rect);

        let info = &series.data.info;
        let entry = &series.data.entry;

        // Series title
        {
            let mut fragments: SmallVec<[Fragment; 2]> = smallvec![Fragment::Span(
                text::bold(&info.title_preferred),
                SpanOptions::new().overflow(OverflowMode::Truncate)
            )];

            if entry.needs_sync() {
                fragments.push(Fragment::span(text::italic(" [*]")));
            }

            let title_widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(title_widget, layout[0]);
        }

        // Items in panel

        macro_rules! draw_stat {
            ($x_column:expr, $y_column:expr => $header:expr, $value:expr) => {{
                let content = layout[1];

                let pos = grid_pos(
                    Rect {
                        x: $x_column,
                        y: $y_column,
                        width: content.width / 3,
                        height: content.height / 3,
                    },
                    content,
                );

                Self::draw_stat($header, $value, pos, frame);
            }};
        }

        // Left panel items

        draw_stat!(0, 0 => "Watch Time", {
            let watch_time_mins = info.episodes * info.episode_length_mins;
            util::hm_from_mins(f32::from(watch_time_mins))
        });

        draw_stat!(0, 1 => "Time Left", {
            let eps_left = info.episodes - entry.watched_episodes().min(info.episodes);
            let time_left_mins = eps_left * info.episode_length_mins;
            util::hm_from_mins(f32::from(time_left_mins))
        });

        draw_stat!(0, 2 => "Episode Length", format!("{}M", info.episode_length_mins));

        // Middle panel items

        draw_stat!(1, 0 => "Progress", format!("{}|{}", entry.watched_episodes(), info.episodes));

        draw_stat!(1, 1 => "Score", {
            match (entry.score(), &state.remote) {
                (Some(score), RemoteStatus::LoggedIn(remote)) => remote.score_to_str(score as u8),
                (Some(score), RemoteStatus::LoggingIn(_)) => score.to_string().into(),
                (None, _) => "??".into(),
            }
        });

        draw_stat!(1, 2 => "Status", {
            let status: &'static str = entry.status().into();
            status
        });

        // Right panel items

        // TODO: allow the format to be changed in the config
        let format_date = |date: Option<SeriesDate>| {
            date.map_or_else(
                || Cow::Borrowed("??"),
                |date| format!("{:02}/{:02}/{:02}", date.month, date.day, date.year % 100).into(),
            )
        };

        draw_stat!(2, 0 => "Start Date", format_date(entry.start_date()));
        draw_stat!(2, 1 => "Finish Date", format_date(entry.end_date()));
        draw_stat!(2, 2 => "Rewatched", entry.times_rewatched().to_string());

        self.draw_status_text(state, layout[2], frame);
    }

    fn draw_stat<B, S>(header: &str, value: S, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        S: AsRef<str>,
    {
        let fragments = [
            Fragment::span(text::bold(header)),
            Fragment::Line,
            Fragment::span(text::italic(value.as_ref())),
        ];

        let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
        frame.render_widget(widget, rect);
    }

    fn draw_status_text<B: Backend>(&self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let progress_remaining_secs = self.progress_remaining_secs.load(Ordering::SeqCst);

        // Remaining time until progression
        if progress_remaining_secs > 0 {
            let mins = (progress_remaining_secs as f32 / 60.0).round() as u32;

            let fragments = [
                Fragment::span(text::bold(mins.to_string())),
                Fragment::span(text::bold(" Minutes Until Progression")),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
        // Login message
        else if let RemoteStatus::LoggingIn(username) = &state.remote {
            let fragments = [
                Fragment::span(text::bold("Logging In As ")),
                Fragment::Span(
                    text::bold_with(username, |s| s.fg(Color::Blue)),
                    SpanOptions::new().overflow(OverflowMode::Truncate),
                ),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
    }

    pub fn draw<B: Backend>(&mut self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let info_block = block::with_borders("Info");
        frame.render_widget(info_block, rect);

        if state.users.get().is_empty() {
            return Self::draw_no_users_info(rect, frame);
        }

        match state.series.selected() {
            Some(LoadedSeries::Complete(series)) => {
                self.draw_series_info(state, series, rect, frame)
            }
            Some(LoadedSeries::Partial(_, err)) => Self::draw_series_error(err, rect, frame),
            Some(LoadedSeries::None(_, err)) => Self::draw_series_error(err, rect, frame),
            None => Self::draw_no_series_found(rect, frame),
        }
    }
}

impl Component for InfoPanel {
    type State = ();
    type KeyResult = ();

    fn process_key(&mut self, _: crate::key::Key, _: &mut Self::State) -> Self::KeyResult {}
}
<commit_msg>anup: Render info panel error / help messages with widgets from the tui-utils library<commit_after>use crate::remote::RemoteStatus;
use crate::tui::state::ProgressTime;
use crate::tui::state::SharedState;
use crate::tui::widget_util::{block, text};
use crate::tui::{state::StateEvent, UIState};
use crate::util;
use crate::{
    series::{LoadedSeries, Series},
    tui::component::Component,
};
use anime::remote::{ScoreParser, SeriesDate};
use chrono::Utc;
use smallvec::{smallvec, SmallVec};
use std::{
    borrow::Cow,
    sync::{atomic::Ordering, Arc},
    time::Duration,
};
use std::{fmt, sync::atomic::AtomicU32};
use tokio::{task, time};
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::Color;
use tui::terminal::Frame;
use tui::text::Span;
use tui_utils::{
    grid_pos,
    widgets::{Fragment, OverflowMode, SimpleText, SpanOptions, TextFragments},
    wrap,
};
use util::ScopedTask;

pub struct InfoPanel {
    progress_remaining_secs: Arc<AtomicU32>,
    #[allow(dead_code)]
    event_monitor_task: ScopedTask<()>,
}

impl InfoPanel {
    pub fn new(state: &SharedState) -> Self {
        let progress_remaining_secs = Arc::new(AtomicU32::default());
        let event_monitor_task =
            Self::spawn_episode_event_monitor(state, Arc::clone(&progress_remaining_secs)).into();

        Self {
            progress_remaining_secs,
            event_monitor_task,
        }
    }

    fn spawn_episode_event_monitor(
        state: &SharedState,
        progress_remaining_secs: Arc<AtomicU32>,
    ) -> task::JoinHandle<()> {
        let state = state.clone();

        task::spawn(async move {
            let mut events = {
                let state = state.lock();
                state.events.subscribe()
            };

            #[allow(unused_variables)]
            let mut progress_task: Option<ScopedTask<_>> = None;

            while let Ok(event) = events.recv().await {
                #[allow(unused_assignments)]
                match event {
                    StateEvent::StartedEpisode(progress_time) => {
                        let state = state.clone();
                        let remaining_secs = Arc::clone(&progress_remaining_secs);

                        let task =
                            Self::spawn_progress_monitor_task(state, remaining_secs, progress_time)
                                .into();

                        progress_task = Some(task);
                    }
                    StateEvent::FinishedEpisode => {
                        progress_remaining_secs.store(0, Ordering::SeqCst);
                        progress_task = None;

                        let mut state = state.lock();
                        state.mark_dirty();
                    }
                }
            }
        })
    }

    fn spawn_progress_monitor_task(
        state: SharedState,
        remaining_secs: Arc<AtomicU32>,
        progress_time: ProgressTime,
    ) -> task::JoinHandle<()> {
        task::spawn(async move {
            let mut first_iter = true;

            loop {
                let cur_progress = remaining_secs.load(Ordering::SeqCst);

                if cur_progress == 0 && !first_iter {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                let diff = progress_time - Utc::now();
                let secs = diff.num_seconds();

                if secs <= 0 {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                remaining_secs.store(secs as u32, Ordering::SeqCst);
                first_iter = false;

                {
                    state.lock().mark_dirty();
                }

                time::sleep(Duration::from_secs(60)).await;
            }
        })
    }

    fn header_body_layout(rect: Rect) -> (Rect, Rect) {
        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(2), Constraint::Percentage(100)])
            .margin(2)
            .split(rect);

        (layout[0], layout[1])
    }

    fn draw_text_panel<'a, B>(
        header: Span,
        body: &[Fragment],
        header_pos: Rect,
        body_pos: Rect,
        frame: &mut Frame<B>,
    ) where
        B: Backend,
    {
        let header_widget = SimpleText::new(header)
            .alignment(Alignment::Center)
            .overflow(OverflowMode::Truncate);

        frame.render_widget(header_widget, header_pos);

        let body_widget = TextFragments::new(body).alignment(Alignment::Center);
        frame.render_widget(body_widget, body_pos);
    }

    fn draw_no_users_info<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let span = |text| {
            Fragment::Span(
                Span::raw(text),
                SpanOptions::new().overflow(OverflowMode::Truncate),
            )
        };

        let body = [
            span("Add an account by pressing 'u' to open"),
            Fragment::Line,
            span("user management and then by pressing tab"),
            Fragment::Line,
            span("to switch to the add user panel."),
            Fragment::Line,
            Fragment::Line,
            span("Then open the auth URL in your browser"),
            Fragment::Line,
            span("by pressing Ctrl + O, and follow its instructions."),
            Fragment::Line,
            Fragment::Line,
            span("Once you have a token, paste it in with either"),
            Fragment::Line,
            span("Ctrl + Shift + V or Ctrl + V."),
            Fragment::Line,
            Fragment::Line,
            span("More detailed instructions here:"),
            Fragment::Line,
            span("https://github.com/Acizza/anup#adding-an-account"),
        ];

        let (hpos, bpos) = Self::header_body_layout(rect);
        Self::draw_text_panel(text::bold("No Accounts Added"), &body, hpos, bpos, frame);
    }

    fn draw_no_series_found<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let span = |text| {
            Fragment::Span(
                Span::raw(text),
                SpanOptions::new().overflow(OverflowMode::Truncate),
            )
        };

        let body = [
            span("Add one by pressing the 'a' key."),
            Fragment::Line,
            Fragment::Line,
            span("The opened panel will require you to specify"),
            Fragment::Line,
            span("a name for the series you want to add."),
            Fragment::Line,
            Fragment::Line,
            span("For automatic detection, the name should be"),
            Fragment::Line,
            span("similar to the name of the folder the series"),
            Fragment::Line,
            span("is in on disk."),
        ];

        let (hpos, bpos) = Self::header_body_layout(rect);
        Self::draw_text_panel(text::bold("No Series Found"), &body, hpos, bpos, frame);
    }

    fn draw_series_error<B, E>(err: E, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        E: fmt::Display,
    {
        let header = text::bold_with("Error Loading Series", |s| s.fg(Color::Red));

        let body = [Fragment::Span(
            text::with_color(err.to_string(), Color::Red),
            SpanOptions::new().overflow(OverflowMode::Truncate),
        )];

        let (hpos, bpos) = Self::header_body_layout(rect);
        let wrapped = wrap::by_letters(body.iter().cloned(), bpos.width);

        Self::draw_text_panel(header, &wrapped, hpos, bpos, frame);
    }

    #[allow(clippy::too_many_lines)]
    fn draw_series_info<B>(
        &self,
        state: &UIState,
        series: &Series,
        rect: Rect,
        frame: &mut Frame<B>,
    ) where
        B: Backend,
    {
        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(rect);

        let info = &series.data.info;
        let entry = &series.data.entry;

        // Series title
        {
            let mut fragments: SmallVec<[Fragment; 2]> = smallvec![Fragment::Span(
                text::bold(&info.title_preferred),
                SpanOptions::new().overflow(OverflowMode::Truncate)
            )];

            if entry.needs_sync() {
                fragments.push(Fragment::span(text::italic(" [*]")));
            }

            let title_widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(title_widget, layout[0]);
        }

        // Items in panel

        macro_rules! draw_stat {
            ($x_column:expr, $y_column:expr => $header:expr, $value:expr) => {{
                let content = layout[1];

                let pos = grid_pos(
                    Rect {
                        x: $x_column,
                        y: $y_column,
                        width: content.width / 3,
                        height: content.height / 3,
                    },
                    content,
                );

                Self::draw_stat($header, $value, pos, frame);
            }};
        }

        // Left panel items

        draw_stat!(0, 0 => "Watch Time", {
            let watch_time_mins = info.episodes * info.episode_length_mins;
            util::hm_from_mins(f32::from(watch_time_mins))
        });

        draw_stat!(0, 1 => "Time Left", {
            let eps_left = info.episodes - entry.watched_episodes().min(info.episodes);
            let time_left_mins = eps_left * info.episode_length_mins;
            util::hm_from_mins(f32::from(time_left_mins))
        });

        draw_stat!(0, 2 => "Episode Length", format!("{}M", info.episode_length_mins));

        // Middle panel items

        draw_stat!(1, 0 => "Progress", format!("{}|{}", entry.watched_episodes(), info.episodes));

        draw_stat!(1, 1 => "Score", {
            match (entry.score(), &state.remote) {
                (Some(score), RemoteStatus::LoggedIn(remote)) => remote.score_to_str(score as u8),
                (Some(score), RemoteStatus::LoggingIn(_)) => score.to_string().into(),
                (None, _) => "??".into(),
            }
        });

        draw_stat!(1, 2 => "Status", {
            let status: &'static str = entry.status().into();
            status
        });

        // Right panel items

        // TODO: allow the format to be changed in the config
        let format_date = |date: Option<SeriesDate>| {
            date.map_or_else(
                || Cow::Borrowed("??"),
                |date| format!("{:02}/{:02}/{:02}", date.month, date.day, date.year % 100).into(),
            )
        };

        draw_stat!(2, 0 => "Start Date", format_date(entry.start_date()));
        draw_stat!(2, 1 => "Finish Date", format_date(entry.end_date()));
        draw_stat!(2, 2 => "Rewatched", entry.times_rewatched().to_string());

        self.draw_status_text(state, layout[2], frame);
    }

    fn draw_stat<B, S>(header: &str, value: S, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        S: AsRef<str>,
    {
        let fragments = [
            Fragment::span(text::bold(header)),
            Fragment::Line,
            Fragment::span(text::italic(value.as_ref())),
        ];

        let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
        frame.render_widget(widget, rect);
    }

    fn draw_status_text<B: Backend>(&self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let progress_remaining_secs = self.progress_remaining_secs.load(Ordering::SeqCst);

        // Remaining time until progression
        if progress_remaining_secs > 0 {
            let mins = (progress_remaining_secs as f32 / 60.0).round() as u32;

            let fragments = [
                Fragment::span(text::bold(mins.to_string())),
                Fragment::span(text::bold(" Minutes Until Progression")),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
        // Login message
        else if let RemoteStatus::LoggingIn(username) = &state.remote {
            let fragments = [
                Fragment::span(text::bold("Logging In As ")),
                Fragment::Span(
                    text::bold_with(username, |s| s.fg(Color::Blue)),
                    SpanOptions::new().overflow(OverflowMode::Truncate),
                ),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
    }

    pub fn draw<B: Backend>(&mut self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let info_block = block::with_borders("Info");
        frame.render_widget(info_block, rect);

        if state.users.get().is_empty() {
            return Self::draw_no_users_info(rect, frame);
        }

        match state.series.selected() {
            Some(LoadedSeries::Complete(series)) => {
                self.draw_series_info(state, series, rect, frame)
            }
            Some(LoadedSeries::Partial(_, err)) => Self::draw_series_error(err, rect, frame),
            Some(LoadedSeries::None(_, err)) => Self::draw_series_error(err, rect, frame),
            None => Self::draw_no_series_found(rect, frame),
        }
    }
}

impl Component for InfoPanel {
    type State = ();
    type KeyResult = ();

    fn process_key(&mut self, _: crate::key::Key, _: &mut Self::State) -> Self::KeyResult {}
}
<|endoftext|>