<commit_before>//! Thread-local storage for JavaScript threads.
//!
//! At runtime, an instance of a Node.js addon can contain its own local storage,
//! which can then be shared and accessed as needed from Rust in a Neon module. This can
//! be useful for setting up long-lived state that needs to be shared between calls
//! of an addon's APIs.
//!
//! For example, an addon may wish to track the [thread ID][threadId] of each of its
//! instances:
//!
//! ```
//! # use neon::prelude::*;
//! # use neon::thread::LocalKey;
//! static THREAD_ID: LocalKey<u32> = LocalKey::new();
//!
//! pub fn thread_id<'cx, C: Context<'cx>>(cx: &mut C) -> NeonResult<u32> {
//!     THREAD_ID.get_or_try_init(cx, |cx| {
//!         let global = cx.global();
//!         let require: Handle<JsFunction> = global.get(cx, "require")?;
//!         let worker: Handle<JsObject> = require.call_with(cx)
//!             .arg(cx.string("node:worker_threads"))
//!             .apply(cx)?;
//!         let thread_id: Handle<JsNumber> = worker.get(cx, "threadId")?;
//!         Ok(thread_id.value(cx) as u32)
//!     }).cloned()
//! }
//! ```
//!
//! ### The Addon Lifecycle
//!
//! For some use cases, a single shared global constant stored in a `static` variable
//! might be sufficient:
//!
//! ```
//! static MY_CONSTANT: &'static str = "hello Neon";
//! ```
//!
//! This variable will be allocated when the addon is first loaded into the Node.js
//! process. This works fine for single-threaded applications, or global immutable
//! data.
//!
//! However, since the addition of [worker threads][workers] in Node v10,
//! modules can be instantiated multiple times in a single Node process. This means
//! that while the dynamically-loaded binary library (i.e., the Rust implementation of
//! the addon) is only loaded once in the running process, but its [`#[main]`](neon::main) function
//! is executed multiple times with distinct module objects, once per application thread:
//!
//! ![The Node.js addon lifecycle, described in detail below.][lifecycle]
//!
//! This means that any thread-local data needs to be initialized separately for each
//! instance of the addon. This module provides a simple container type, [`LocalKey`](LocalKey),
//! for allocating and initializing thread-local data. (Technically, this data is stored in the
//! addon's [module instance][environment], which is equivalent to being thread-local.)
//!
//! A common example is when an addon defines a custom datatype, which cannot be used across
//! separate threads. The constructor for the datatype can be [rooted](crate::handle::Root) and
//! saved in thread-local storage:
//!
//! ```
//! # use neon::prelude::*;
//! # use neon::thread::LocalKey;
//! # fn initialize_my_datatype<'cx, C: Context<'cx>>(cx: &mut C) -> JsResult<'cx, JsFunction> { unimplemented!() }
//! static MY_CONSTRUCTOR: LocalKey<Root<JsFunction>> = LocalKey::new();
//!
//! pub fn my_constructor<'cx, C: Context<'cx>>(cx: &mut C) -> JsResult<'cx, JsFunction> {
//!     let constructor = MY_CONSTRUCTOR.get_or_try_init(cx, |cx| {
//!         let constructor: Handle<JsFunction> = initialize_my_datatype(cx)?;
//!         Ok(constructor.root(cx))
//!     })?;
//!     Ok(constructor.to_inner(cx))
//! }
//! ```
//!
//! Notice that if this code were implemented without a `LocalKey`, it would fail whenever
//! one thread stores an instance of the constructor and a different thread attempts to
//! access it with the call to [`to_inner()`](crate::handle::Root::to_inner).
//!
//! ### When to Use Thread-Local Storage
//!
//! Single-threaded applications don't generally need to worry about thread-local data.
//! There are two cases where Neon apps should consider storing static data in a
//! `LocalKey` storage cell:
//!
//! - **Multi-threaded applications:** If your Node application uses the `Worker`
//!   API, you'll want to store any static data that might get access from multiple
//!   threads in thread-local data.
//! - **Libraries:** If your addon is part of a library that could be used by multiple
//!   applications, you'll want to store static data in thread-local data in case the
//!   addon ends up instantiated by multiple threads in some future application.
//!
//! ### Why Not Use Standard TLS?
//!
//! Because the JavaScript engine may not tie JavaScript threads 1:1 to system threads,
//! it is recommended to use this module instead of the Rust standard thread-local storage
//! when tying data to a JavaScript thread.
//!
//! [environment]: https://nodejs.org/api/n-api.html#environment-life-cycle-apis
//! [lifecycle]: https://raw.githubusercontent.com/neon-bindings/neon/main/doc/lifecycle.png
//! [workers]: https://nodejs.org/api/worker_threads.html
//! [threadId]: https://nodejs.org/api/worker_threads.html#workerthreadid

use std::any::Any;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicUsize, Ordering};

use once_cell::sync::OnceCell;

use crate::context::Context;
use crate::lifecycle::LocalCell;

static COUNTER: AtomicUsize = AtomicUsize::new(0);

fn next_id() -> usize {
    COUNTER.fetch_add(1, Ordering::SeqCst)
}

/// A JavaScript thread-local container that owns its contents, similar to
/// [`std::thread::LocalKey`](std::thread::LocalKey) but tied to a JavaScript thread rather
/// than a system thread.
///
/// ### Initialization and Destruction
///
/// Initialization is dynamically performed on the first call to one of the `init` methods
/// of `LocalKey`, and values that implement [`Drop`](std::ops::Drop) get destructed when
/// the JavaScript thread exits, i.e. when a worker thread terminates or the main thread
/// terminates on process exit.
#[derive(Default)]
pub struct LocalKey<T> {
    _type: PhantomData<T>,
    id: OnceCell<usize>,
}

impl<T> LocalKey<T> {
    /// Creates a new local value. This method is `const`, so it can be assigned to
    /// static variables.
    pub const fn new() -> Self {
        Self {
            _type: PhantomData,
            id: OnceCell::new(),
        }
    }

    fn id(&self) -> usize {
        *self.id.get_or_init(next_id)
    }
}

impl<T: Any + Send + 'static> LocalKey<T> {
    /// Gets the current value of the cell. Returns `None` if the cell has not
    /// yet been initialized.
    pub fn get<'cx, 'a, C>(&self, cx: &'a mut C) -> Option<&'cx T>
    where
        C: Context<'cx>,
    {
        // Unwrap safety: The type bound LocalKey<T> and the fact that every LocalKey has a unique
        // id guarantees that the cell is only ever assigned instances of type T.
        let r: Option<&T> =
            LocalCell::get(cx, self.id()).map(|value| value.downcast_ref().unwrap());

        // Safety: Since the Box is immutable and heap-allocated, it's guaranteed not to
        // move or change for the duration of the context.
        unsafe { std::mem::transmute::<Option<&'a T>, Option<&'cx T>>(r) }
    }

    /// Gets the current value of the cell, initializing it with the result of
    /// calling `f` if it has not yet been initialized.
    pub fn get_or_init<'cx, 'a, C, F>(&self, cx: &'a mut C, f: F) -> &'cx T
    where
        C: Context<'cx>,
        F: FnOnce() -> T,
    {
        // Unwrap safety: The type bound LocalKey<T> and the fact that every LocalKey has a unique
        // id guarantees that the cell is only ever assigned instances of type T.
        let r: &T = LocalCell::get_or_init(cx, self.id(), || Box::new(f()))
            .downcast_ref()
            .unwrap();

        // Safety: Since the Box is immutable and heap-allocated, it's guaranteed not to
        // move or change for the duration of the context.
        unsafe { std::mem::transmute::<&'a T, &'cx T>(r) }
    }

    /// Gets the current value of the cell, initializing it with the result of
    /// calling `f` if it has not yet been initialized. Returns `Err` if the
    /// callback triggers a JavaScript exception.
    ///
    /// # Panics
    ///
    /// During the execution of `f`, calling any methods on this `LocalKey` that
    /// attempt to initialize it will panic.
    pub fn get_or_try_init<'cx, 'a, C, E, F>(&self, cx: &'a mut C, f: F) -> Result<&'cx T, E>
    where
        C: Context<'cx>,
        F: FnOnce(&mut C) -> Result<T, E>,
    {
        // Unwrap safety: The type bound LocalKey<T> and the fact that every LocalKey has a unique
        // id guarantees that the cell is only ever assigned instances of type T.
        let r: &T = LocalCell::get_or_try_init(cx, self.id(), |cx| Ok(Box::new(f(cx)?)))?
            .downcast_ref()
            .unwrap();

        // Safety: Since the Box is immutable and heap-allocated, it's guaranteed not to
        // move or change for the duration of the context.
        Ok(unsafe { std::mem::transmute::<&'a T, &'cx T>(r) })
    }
}

impl<T: Any + Send + Default + 'static> LocalKey<T> {
    /// Gets the current value of the cell, initializing it with the default value
    /// if it has not yet been initialized.
    pub fn get_or_init_default<'cx, 'a, C>(&self, cx: &'a mut C) -> &'cx T
    where
        C: Context<'cx>,
    {
        self.get_or_init(cx, Default::default)
    }
}
<commit_msg>Clarifying doc text about cross-thread sharing<commit_after>//! Thread-local storage for JavaScript threads.
//!
//! At runtime, an instance of a Node.js addon can contain its own local storage,
//! which can then be shared and accessed as needed from Rust in a Neon module. This can
//! be useful for setting up long-lived state that needs to be shared between calls
//! of an addon's APIs.
//!
//! For example, an addon may wish to track the [thread ID][threadId] of each of its
//! instances:
//!
//! ```
//! # use neon::prelude::*;
//! # use neon::thread::LocalKey;
//! static THREAD_ID: LocalKey<u32> = LocalKey::new();
//!
//! pub fn thread_id<'cx, C: Context<'cx>>(cx: &mut C) -> NeonResult<u32> {
//!     THREAD_ID.get_or_try_init(cx, |cx| {
//!         let global = cx.global();
//!         let require: Handle<JsFunction> = global.get(cx, "require")?;
//!         let worker: Handle<JsObject> = require.call_with(cx)
//!             .arg(cx.string("node:worker_threads"))
//!             .apply(cx)?;
//!         let thread_id: Handle<JsNumber> = worker.get(cx, "threadId")?;
//!         Ok(thread_id.value(cx) as u32)
//!     }).cloned()
//! }
//! ```
//!
//! ### The Addon Lifecycle
//!
//! For some use cases, a single shared global constant stored in a `static` variable
//! might be sufficient:
//!
//! ```
//! static MY_CONSTANT: &'static str = "hello Neon";
//! ```
//!
//! This variable will be allocated when the addon is first loaded into the Node.js
//! process. This works fine for single-threaded applications, or global immutable
//! data.
//!
//! However, since the addition of [worker threads][workers] in Node v10,
//! modules can be instantiated multiple times in a single Node process. This means
//! that while the dynamically-loaded binary library (i.e., the Rust implementation of
//! the addon) is only loaded once in the running process, but its [`#[main]`](neon::main) function
//! is executed multiple times with distinct module objects, once per application thread:
//!
//! ![The Node.js addon lifecycle, described in detail below.][lifecycle]
//!
//! This means that any thread-local data needs to be initialized separately for each
//! instance of the addon. This module provides a simple container type, [`LocalKey`](LocalKey),
//! for allocating and initializing thread-local data. (Technically, this data is stored in the
//! addon's [module instance][environment], which is equivalent to being thread-local.)
//!
//! A common example is when an addon needs to maintain a reference to a JavaScript value. References cannot
//! be used across separate threads. The constructor for the datatype can be [rooted](crate::handle::Root) and
//! saved in thread-local storage:
//!
//! ```
//! # use neon::prelude::*;
//! # use neon::thread::LocalKey;
//! # fn initialize_my_datatype<'cx, C: Context<'cx>>(cx: &mut C) -> JsResult<'cx, JsFunction> { unimplemented!() }
//! static MY_CONSTRUCTOR: LocalKey<Root<JsFunction>> = LocalKey::new();
//!
//! pub fn my_constructor<'cx, C: Context<'cx>>(cx: &mut C) -> JsResult<'cx, JsFunction> {
//!     let constructor = MY_CONSTRUCTOR.get_or_try_init(cx, |cx| {
//!         let constructor: Handle<JsFunction> = initialize_my_datatype(cx)?;
//!         Ok(constructor.root(cx))
//!     })?;
//!     Ok(constructor.to_inner(cx))
//! }
//! ```
//!
//! Notice that if this code were implemented without a `LocalKey`, it would fail whenever
//! one thread stores an instance of the constructor and a different thread attempts to
//! access it with the call to [`to_inner()`](crate::handle::Root::to_inner).
//!
//! ### When to Use Thread-Local Storage
//!
//! Single-threaded applications don't generally need to worry about thread-local data.
//! There are two cases where Neon apps should consider storing static data in a
//! `LocalKey` storage cell:
//!
//! - **Multi-threaded applications:** If your Node application uses the `Worker`
//!   API, you'll want to store any static data that might get access from multiple
//!   threads in thread-local data.
//! - **Libraries:** If your addon is part of a library that could be used by multiple
//!   applications, you'll want to store static data in thread-local data in case the
//!   addon ends up instantiated by multiple threads in some future application.
//!
//! ### Why Not Use Standard TLS?
//!
//! Because the JavaScript engine may not tie JavaScript threads 1:1 to system threads,
//! it is recommended to use this module instead of the Rust standard thread-local storage
//! when tying data to a JavaScript thread.
//!
//! [environment]: https://nodejs.org/api/n-api.html#environment-life-cycle-apis
//! [lifecycle]: https://raw.githubusercontent.com/neon-bindings/neon/main/doc/lifecycle.png
//! [workers]: https://nodejs.org/api/worker_threads.html
//! [threadId]: https://nodejs.org/api/worker_threads.html#workerthreadid

use std::any::Any;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicUsize, Ordering};

use once_cell::sync::OnceCell;

use crate::context::Context;
use crate::lifecycle::LocalCell;

static COUNTER: AtomicUsize = AtomicUsize::new(0);

fn next_id() -> usize {
    COUNTER.fetch_add(1, Ordering::SeqCst)
}

/// A JavaScript thread-local container that owns its contents, similar to
/// [`std::thread::LocalKey`](std::thread::LocalKey) but tied to a JavaScript thread rather
/// than a system thread.
///
/// ### Initialization and Destruction
///
/// Initialization is dynamically performed on the first call to one of the `init` methods
/// of `LocalKey`, and values that implement [`Drop`](std::ops::Drop) get destructed when
/// the JavaScript thread exits, i.e. when a worker thread terminates or the main thread
/// terminates on process exit.
#[derive(Default)]
pub struct LocalKey<T> {
    _type: PhantomData<T>,
    id: OnceCell<usize>,
}

impl<T> LocalKey<T> {
    /// Creates a new local value. This method is `const`, so it can be assigned to
    /// static variables.
    pub const fn new() -> Self {
        Self {
            _type: PhantomData,
            id: OnceCell::new(),
        }
    }

    fn id(&self) -> usize {
        *self.id.get_or_init(next_id)
    }
}

impl<T: Any + Send + 'static> LocalKey<T> {
    /// Gets the current value of the cell. Returns `None` if the cell has not
    /// yet been initialized.
    pub fn get<'cx, 'a, C>(&self, cx: &'a mut C) -> Option<&'cx T>
    where
        C: Context<'cx>,
    {
        // Unwrap safety: The type bound LocalKey<T> and the fact that every LocalKey has a unique
        // id guarantees that the cell is only ever assigned instances of type T.
        let r: Option<&T> =
            LocalCell::get(cx, self.id()).map(|value| value.downcast_ref().unwrap());

        // Safety: Since the Box is immutable and heap-allocated, it's guaranteed not to
        // move or change for the duration of the context.
        unsafe { std::mem::transmute::<Option<&'a T>, Option<&'cx T>>(r) }
    }

    /// Gets the current value of the cell, initializing it with the result of
    /// calling `f` if it has not yet been initialized.
    pub fn get_or_init<'cx, 'a, C, F>(&self, cx: &'a mut C, f: F) -> &'cx T
    where
        C: Context<'cx>,
        F: FnOnce() -> T,
    {
        // Unwrap safety: The type bound LocalKey<T> and the fact that every LocalKey has a unique
        // id guarantees that the cell is only ever assigned instances of type T.
        let r: &T = LocalCell::get_or_init(cx, self.id(), || Box::new(f()))
            .downcast_ref()
            .unwrap();

        // Safety: Since the Box is immutable and heap-allocated, it's guaranteed not to
        // move or change for the duration of the context.
        unsafe { std::mem::transmute::<&'a T, &'cx T>(r) }
    }

    /// Gets the current value of the cell, initializing it with the result of
    /// calling `f` if it has not yet been initialized. Returns `Err` if the
    /// callback triggers a JavaScript exception.
    ///
    /// # Panics
    ///
    /// During the execution of `f`, calling any methods on this `LocalKey` that
    /// attempt to initialize it will panic.
    pub fn get_or_try_init<'cx, 'a, C, E, F>(&self, cx: &'a mut C, f: F) -> Result<&'cx T, E>
    where
        C: Context<'cx>,
        F: FnOnce(&mut C) -> Result<T, E>,
    {
        // Unwrap safety: The type bound LocalKey<T> and the fact that every LocalKey has a unique
        // id guarantees that the cell is only ever assigned instances of type T.
        let r: &T = LocalCell::get_or_try_init(cx, self.id(), |cx| Ok(Box::new(f(cx)?)))?
            .downcast_ref()
            .unwrap();

        // Safety: Since the Box is immutable and heap-allocated, it's guaranteed not to
        // move or change for the duration of the context.
        Ok(unsafe { std::mem::transmute::<&'a T, &'cx T>(r) })
    }
}

impl<T: Any + Send + Default + 'static> LocalKey<T> {
    /// Gets the current value of the cell, initializing it with the default value
    /// if it has not yet been initialized.
    pub fn get_or_init_default<'cx, 'a, C>(&self, cx: &'a mut C) -> &'cx T
    where
        C: Context<'cx>,
    {
        self.get_or_init(cx, Default::default)
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2018, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#![feature(duration_float)]

use std::io::BufRead;

use regex::Regex;

use protobuf::Message;

fn main() {
    let mut args = std::env::args();
    args.next();

    let log_file = args.next().unwrap();
    let trace_file = args.next().unwrap();

    let inp = std::fs::File::open(log_file).unwrap();
    let mut out = std::fs::File::create(trace_file).unwrap();

    let mut trace = quic_trace::Trace::new();

    trace.set_protocol_version(b"AAAA".to_vec());

    // Log prefix.
    let prefix_re = Regex::new(r"^\[(.*) TRACE quiche.*\]").unwrap();

    // Packet events.
    let pkt_re = Regex::new(
        r"(rx|tx) pkt (Initial|Handshake|Application) .* len=(\d+) pn=(\d+)",
    )
    .unwrap();
    let lost_re = Regex::new(r"packet (\d+) lost on epoch (\d)").unwrap();

    let rec_re =
        Regex::new(r"timer=(.*) crypto=(.*) inflight=(.*) cwnd=(.*) latest_rtt=(.*) srtt=(.*) min_rtt=(.*) rttvar=(.*) probes=(\d+)").unwrap();

    // Frame events.
    let stream_frm_re = Regex::new(
        r"(rx|tx) frm STREAM id=(\d+) off=(\d+) len=(\d+) fin=(true|false)",
    )
    .unwrap();
    let ack_frm_re =
        Regex::new(r"(rx|tx) frm ACK delay=(.*) blocks=\[(.*)\]").unwrap();
    let close_frm_re = Regex::new(
        r"(rx|tx) frm (APPLICATION|CONNECTION)_CLOSE err=(\d+) reason=",
    )
    .unwrap();

    let mut start_time = None;

    let mut events = Vec::new();

    let mut event: Option<quic_trace::Event> = None;

    let file = std::io::BufReader::new(&inp);
    for (_, line) in file.lines().enumerate() {
        let l = line.unwrap();

        let time = match prefix_re.captures(&l) {
            Some(caps) => {
                let s = caps.get(1).unwrap().as_str();
                humantime::parse_rfc3339(s).unwrap()
            },

            None => continue,
        };

        if start_time.is_none() {
            start_time = Some(time);
        }

        if let Some(caps) = pkt_re.captures(&l) {
            // Flush previous event.
            if event.is_some() {
                events.push(event.unwrap());
            }

            let mut ev = quic_trace::Event::new();

            let time_us = time.duration_since(start_time.unwrap()).unwrap();
            ev.set_time_us(time_us.as_micros() as u64);

            let ty = match caps.get(1).unwrap().as_str() {
                "rx" => quic_trace::EventType::PACKET_RECEIVED,
                "tx" => quic_trace::EventType::PACKET_SENT,
                _ => unreachable!(),
            };
            ev.set_event_type(ty);

            let ty = caps.get(2).unwrap().as_str();
            ev.set_encryption_level(str_to_enc_level(ty));

            let len = caps.get(3).unwrap().as_str();
            ev.set_packet_size(len.parse::<u64>().unwrap());

            let pn = caps.get(4).unwrap().as_str();
            ev.set_packet_number(pn.parse::<u64>().unwrap());

            event = Some(ev);
            continue;
        }

        if let Some(caps) = lost_re.captures(&l) {
            let mut ev = quic_trace::Event::new();

            let time_us = time.duration_since(start_time.unwrap()).unwrap();
            ev.set_time_us(time_us.as_micros() as u64);

            ev.set_event_type(quic_trace::EventType::PACKET_LOST);

            let pn = caps.get(1).unwrap().as_str();
            ev.set_packet_number(pn.parse::<u64>().unwrap());

            let ty = caps.get(2).unwrap().as_str().parse::<u64>().unwrap();
            ev.set_encryption_level(int_to_enc_level(ty));

            events.push(ev);
            continue;
        }

        if let Some(caps) = rec_re.captures(&l) {
            if event.is_none() {
                unreachable!();
            }

            let mut state = quic_trace::TransportState::new();

            let inflight = caps.get(3).unwrap().as_str();
            state.set_in_flight_bytes(inflight.parse::<u64>().unwrap());

            let cwnd = caps.get(4).unwrap().as_str();
            state.set_cwnd_bytes(cwnd.parse::<u64>().unwrap());

            let latest_rtt = caps.get(5).unwrap().as_str();
            let latest_rtt = str_to_duration(latest_rtt);
            state.set_last_rtt_us(latest_rtt.as_micros() as u64);

            let srtt = caps.get(6).unwrap().as_str();
            let srtt = if srtt == "None" {
                std::time::Duration::from_micros(0)
            } else {
                let srtt = &srtt[5..srtt.len() - 1];
                str_to_duration(srtt)
            };
            state.set_smoothed_rtt_us(srtt.as_micros() as u64);

            let min_rtt = caps.get(7).unwrap().as_str();
            let min_rtt = str_to_duration(min_rtt);
            state.set_smoothed_rtt_us(min_rtt.as_micros() as u64);

            event.as_mut().unwrap().set_transport_state(state);
            continue;
        }

        if let Some(caps) = stream_frm_re.captures(&l) {
            let mut frame = quic_trace::Frame::new();
            frame.set_frame_type(quic_trace::FrameType::STREAM);

            let mut info = quic_trace::StreamFrameInfo::new();

            let id = caps.get(2).unwrap().as_str();
            info.set_stream_id(id.parse::<u64>().unwrap());

            let off = caps.get(3).unwrap().as_str();
            info.set_offset(off.parse::<u64>().unwrap());

            let len = caps.get(4).unwrap().as_str();
            info.set_length(len.parse::<u64>().unwrap());

            let fin = caps.get(5).unwrap().as_str();
            match fin {
                "true" => info.set_fin(true),
                "false" => info.set_fin(false),
                _ => unreachable!(),
            }

            frame.set_stream_frame_info(info);

            event.as_mut().unwrap().mut_frames().push(frame);
            continue;
        }

        if let Some(caps) = ack_frm_re.captures(&l) {
            let mut frame = quic_trace::Frame::new();
            frame.set_frame_type(quic_trace::FrameType::ACK);

            let mut info = quic_trace::AckInfo::new();

            let delay = caps.get(2).unwrap().as_str();
            let delay = delay.parse::<u64>().unwrap() * 2_u64.pow(3_u32);
            info.set_ack_delay_us(delay);

            let mut blocks = Vec::new();

            let ranges = caps.get(3).unwrap().as_str();
            for r in ranges.split(", ") {
                let mut block = quic_trace::AckBlock::new();

                let mut parts = r.split("..");
                block.set_first_packet(
                    parts.next().unwrap().parse::<u64>().unwrap(),
                );
                block.set_last_packet(
                    parts.next().unwrap().parse::<u64>().unwrap(),
                );

                blocks.push(block);
            }

            info.set_acked_packets(protobuf::RepeatedField::from_vec(blocks));

            frame.set_ack_info(info);

            event.as_mut().unwrap().mut_frames().push(frame);
            continue;
        }

        if let Some(caps) = close_frm_re.captures(&l) {
            let mut frame = quic_trace::Frame::new();
            frame.set_frame_type(quic_trace::FrameType::CONNECTION_CLOSE);

            let mut info = quic_trace::CloseInfo::new();

            let err = caps.get(3).unwrap().as_str();
            info.set_error_code(u32::from_str_radix(err, 16).unwrap());

            frame.set_close_info(info);

            event.as_mut().unwrap().mut_frames().push(frame);
            continue;
        }
    }

    println!("Generated {} events", events.len());

    trace.set_events(protobuf::RepeatedField::from_vec(events));

    let mut cos = protobuf::CodedOutputStream::new(&mut out);
    trace.write_to(&mut cos).unwrap();
    cos.flush().unwrap();
}

fn str_to_enc_level(ty: &str) -> quic_trace::EncryptionLevel {
    match ty {
        "Initial" => quic_trace::EncryptionLevel::ENCRYPTION_INITIAL,
        "Handshake" => quic_trace::EncryptionLevel::ENCRYPTION_HANDSHAKE,
        "Application" => quic_trace::EncryptionLevel::ENCRYPTION_1RTT,
        _ => unreachable!(),
    }
}

fn int_to_enc_level(ty: u64) -> quic_trace::EncryptionLevel {
    match ty {
        0 => quic_trace::EncryptionLevel::ENCRYPTION_INITIAL,
        1 => quic_trace::EncryptionLevel::ENCRYPTION_HANDSHAKE,
        2 => quic_trace::EncryptionLevel::ENCRYPTION_1RTT,
        _ => unreachable!(),
    }
}

fn str_to_duration(d: &str) -> std::time::Duration {
    if let Ok(d) = humantime::parse_duration(d) {
        return d;
    }

    // humantime doesn't support parsing float duration, so do it manually.
    let end = d.chars().position(|c| !c.is_numeric() && c != '.').unwrap();
    let num = (&d[..end]).parse::<f64>().unwrap();
    let unit = &d[end..];

    let num = match unit {
        "s" => num,
        "ms" => num / 1_000_f64,
        "us" => num / 1_000_000_f64,
        "µs" => num / 1_000_000_f64,
        "ns" => num / 1_000_000_000_f64,
        _ => unreachable!(),
    };

    std::time::Duration::from_secs_f64(num)
}

mod quic_trace;
<commit_msg>quic-trace-log: duration_float is now a stable feature<commit_after>// Copyright (C) 2018, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::io::BufRead;

use regex::Regex;

use protobuf::Message;

fn main() {
    let mut args = std::env::args();
    args.next();

    let log_file = args.next().unwrap();
    let trace_file = args.next().unwrap();

    let inp = std::fs::File::open(log_file).unwrap();
    let mut out = std::fs::File::create(trace_file).unwrap();

    let mut trace = quic_trace::Trace::new();

    trace.set_protocol_version(b"AAAA".to_vec());

    // Log prefix.
    let prefix_re = Regex::new(r"^\[(.*) TRACE quiche.*\]").unwrap();

    // Packet events.
    let pkt_re = Regex::new(
        r"(rx|tx) pkt (Initial|Handshake|Application) .* len=(\d+) pn=(\d+)",
    )
    .unwrap();
    let lost_re = Regex::new(r"packet (\d+) lost on epoch (\d)").unwrap();

    let rec_re =
        Regex::new(r"timer=(.*) crypto=(.*) inflight=(.*) cwnd=(.*) latest_rtt=(.*) srtt=(.*) min_rtt=(.*) rttvar=(.*) probes=(\d+)").unwrap();

    // Frame events.
    let stream_frm_re = Regex::new(
        r"(rx|tx) frm STREAM id=(\d+) off=(\d+) len=(\d+) fin=(true|false)",
    )
    .unwrap();
    let ack_frm_re =
        Regex::new(r"(rx|tx) frm ACK delay=(.*) blocks=\[(.*)\]").unwrap();
    let close_frm_re = Regex::new(
        r"(rx|tx) frm (APPLICATION|CONNECTION)_CLOSE err=(\d+) reason=",
    )
    .unwrap();

    let mut start_time = None;

    let mut events = Vec::new();

    let mut event: Option<quic_trace::Event> = None;

    let file = std::io::BufReader::new(&inp);
    for (_, line) in file.lines().enumerate() {
        let l = line.unwrap();

        let time = match prefix_re.captures(&l) {
            Some(caps) => {
                let s = caps.get(1).unwrap().as_str();
                humantime::parse_rfc3339(s).unwrap()
            },

            None => continue,
        };

        if start_time.is_none() {
            start_time = Some(time);
        }

        if let Some(caps) = pkt_re.captures(&l) {
            // Flush previous event.
            if event.is_some() {
                events.push(event.unwrap());
            }

            let mut ev = quic_trace::Event::new();

            let time_us = time.duration_since(start_time.unwrap()).unwrap();
            ev.set_time_us(time_us.as_micros() as u64);

            let ty = match caps.get(1).unwrap().as_str() {
                "rx" => quic_trace::EventType::PACKET_RECEIVED,
                "tx" => quic_trace::EventType::PACKET_SENT,
                _ => unreachable!(),
            };
            ev.set_event_type(ty);

            let ty = caps.get(2).unwrap().as_str();
            ev.set_encryption_level(str_to_enc_level(ty));

            let len = caps.get(3).unwrap().as_str();
            ev.set_packet_size(len.parse::<u64>().unwrap());

            let pn = caps.get(4).unwrap().as_str();
            ev.set_packet_number(pn.parse::<u64>().unwrap());

            event = Some(ev);
            continue;
        }

        if let Some(caps) = lost_re.captures(&l) {
            let mut ev = quic_trace::Event::new();

            let time_us = time.duration_since(start_time.unwrap()).unwrap();
            ev.set_time_us(time_us.as_micros() as u64);

            ev.set_event_type(quic_trace::EventType::PACKET_LOST);

            let pn = caps.get(1).unwrap().as_str();
            ev.set_packet_number(pn.parse::<u64>().unwrap());

            let ty = caps.get(2).unwrap().as_str().parse::<u64>().unwrap();
            ev.set_encryption_level(int_to_enc_level(ty));

            events.push(ev);
            continue;
        }

        if let Some(caps) = rec_re.captures(&l) {
            if event.is_none() {
                unreachable!();
            }

            let mut state = quic_trace::TransportState::new();

            let inflight = caps.get(3).unwrap().as_str();
            state.set_in_flight_bytes(inflight.parse::<u64>().unwrap());

            let cwnd = caps.get(4).unwrap().as_str();
            state.set_cwnd_bytes(cwnd.parse::<u64>().unwrap());

            let latest_rtt = caps.get(5).unwrap().as_str();
            let latest_rtt = str_to_duration(latest_rtt);
            state.set_last_rtt_us(latest_rtt.as_micros() as u64);

            let srtt = caps.get(6).unwrap().as_str();
            let srtt = if srtt == "None" {
                std::time::Duration::from_micros(0)
            } else {
                let srtt = &srtt[5..srtt.len() - 1];
                str_to_duration(srtt)
            };
            state.set_smoothed_rtt_us(srtt.as_micros() as u64);

            let min_rtt = caps.get(7).unwrap().as_str();
            let min_rtt = str_to_duration(min_rtt);
            state.set_smoothed_rtt_us(min_rtt.as_micros() as u64);

            event.as_mut().unwrap().set_transport_state(state);
            continue;
        }

        if let Some(caps) = stream_frm_re.captures(&l) {
            let mut frame = quic_trace::Frame::new();
            frame.set_frame_type(quic_trace::FrameType::STREAM);

            let mut info = quic_trace::StreamFrameInfo::new();

            let id = caps.get(2).unwrap().as_str();
            info.set_stream_id(id.parse::<u64>().unwrap());

            let off = caps.get(3).unwrap().as_str();
            info.set_offset(off.parse::<u64>().unwrap());

            let len = caps.get(4).unwrap().as_str();
            info.set_length(len.parse::<u64>().unwrap());

            let fin = caps.get(5).unwrap().as_str();
            match fin {
                "true" => info.set_fin(true),
                "false" => info.set_fin(false),
                _ => unreachable!(),
            }

            frame.set_stream_frame_info(info);

            event.as_mut().unwrap().mut_frames().push(frame);
            continue;
        }

        if let Some(caps) = ack_frm_re.captures(&l) {
            let mut frame = quic_trace::Frame::new();
            frame.set_frame_type(quic_trace::FrameType::ACK);

            let mut info = quic_trace::AckInfo::new();

            let delay = caps.get(2).unwrap().as_str();
            let delay = delay.parse::<u64>().unwrap() * 2_u64.pow(3_u32);
            info.set_ack_delay_us(delay);

            let mut blocks = Vec::new();

            let ranges = caps.get(3).unwrap().as_str();
            for r in ranges.split(", ") {
                let mut block = quic_trace::AckBlock::new();

                let mut parts = r.split("..");
                block.set_first_packet(
                    parts.next().unwrap().parse::<u64>().unwrap(),
                );
                block.set_last_packet(
                    parts.next().unwrap().parse::<u64>().unwrap(),
                );

                blocks.push(block);
            }

            info.set_acked_packets(protobuf::RepeatedField::from_vec(blocks));

            frame.set_ack_info(info);

            event.as_mut().unwrap().mut_frames().push(frame);
            continue;
        }

        if let Some(caps) = close_frm_re.captures(&l) {
            let mut frame = quic_trace::Frame::new();
            frame.set_frame_type(quic_trace::FrameType::CONNECTION_CLOSE);

            let mut info = quic_trace::CloseInfo::new();

            let err = caps.get(3).unwrap().as_str();
            info.set_error_code(u32::from_str_radix(err, 16).unwrap());

            frame.set_close_info(info);

            event.as_mut().unwrap().mut_frames().push(frame);
            continue;
        }
    }

    println!("Generated {} events", events.len());

    trace.set_events(protobuf::RepeatedField::from_vec(events));

    let mut cos = protobuf::CodedOutputStream::new(&mut out);
    trace.write_to(&mut cos).unwrap();
    cos.flush().unwrap();
}

fn str_to_enc_level(ty: &str) -> quic_trace::EncryptionLevel {
    match ty {
        "Initial" => quic_trace::EncryptionLevel::ENCRYPTION_INITIAL,
        "Handshake" => quic_trace::EncryptionLevel::ENCRYPTION_HANDSHAKE,
        "Application" => quic_trace::EncryptionLevel::ENCRYPTION_1RTT,
        _ => unreachable!(),
    }
}

fn int_to_enc_level(ty: u64) -> quic_trace::EncryptionLevel {
    match ty {
        0 => quic_trace::EncryptionLevel::ENCRYPTION_INITIAL,
        1 => quic_trace::EncryptionLevel::ENCRYPTION_HANDSHAKE,
        2 => quic_trace::EncryptionLevel::ENCRYPTION_1RTT,
        _ => unreachable!(),
    }
}

fn str_to_duration(d: &str) -> std::time::Duration {
    if let Ok(d) = humantime::parse_duration(d) {
        return d;
    }

    // humantime doesn't support parsing float duration, so do it manually.
    let end = d.chars().position(|c| !c.is_numeric() && c != '.').unwrap();
    let num = (&d[..end]).parse::<f64>().unwrap();
    let unit = &d[end..];

    let num = match unit {
        "s" => num,
        "ms" => num / 1_000_f64,
        "us" => num / 1_000_000_f64,
        "µs" => num / 1_000_000_f64,
        "ns" => num / 1_000_000_000_f64,
        _ => unreachable!(),
    };

    std::time::Duration::from_secs_f64(num)
}

mod quic_trace;
<|endoftext|>


<commit_before>use std::ffi::{OsString, OsStr};

use ops::{self, Compilation};
use util::{self, CargoTestError, Test, ProcessError};
use util::errors::CargoResult;
use core::Workspace;

pub struct TestOptions<'a> {
    pub compile_opts: ops::CompileOptions<'a>,
    pub no_run: bool,
    pub no_fail_fast: bool,
    pub only_doc: bool,
}

pub fn run_tests(ws: &Workspace,
                 options: &TestOptions,
                 test_args: &[String]) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        return Ok(None)
    }
    let (test, mut errors) = if options.only_doc {
        assert!(options.compile_opts.filter.is_specific());
        run_doc_tests(options, test_args, &compilation)?
    } else {
        run_unit_tests(options, test_args, &compilation)?
    };

    // If we have an error and want to fail fast, return
    if !errors.is_empty() && !options.no_fail_fast {
        return Ok(Some(CargoTestError::new(test, errors)))
    }

    // If a specific test was requested or we're not running any tests at all,
    // don't run any doc tests.
    if options.compile_opts.filter.is_specific() {
        match errors.len() {
            0 => return Ok(None),
            _ => return Ok(Some(CargoTestError::new(test, errors)))
        }
    }

    let (doctest, docerrors) = run_doc_tests(options, test_args, &compilation)?;
    let test = if docerrors.is_empty() { test } else { doctest };
    errors.extend(docerrors);
    if errors.is_empty() {
        Ok(None)
    } else {
        Ok(Some(CargoTestError::new(test, errors)))
    }
}

pub fn run_benches(ws: &Workspace,
                   options: &TestOptions,
                   args: &[String]) -> CargoResult<Option<CargoTestError>> {
    let mut args = args.to_vec();
    args.push("--bench".to_string());
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        return Ok(None)
    }
    let (test, errors) = run_unit_tests(options, &args, &compilation)?;
    match errors.len() {
        0 => Ok(None),
        _ => Ok(Some(CargoTestError::new(test, errors))),
    }
}

fn compile_tests<'a>(ws: &Workspace<'a>,
                     options: &TestOptions<'a>)
                     -> CargoResult<Compilation<'a>> {
    let mut compilation = ops::compile(ws, &options.compile_opts)?;
    compilation.tests.sort_by(|a, b| {
        (a.0.package_id(), &a.1, &a.2).cmp(&(b.0.package_id(), &b.1, &b.2))
    });
    Ok(compilation)
}

/// Run the unit and integration tests of a project.
fn run_unit_tests(options: &TestOptions,
                  test_args: &[String],
                  compilation: &Compilation)
                  -> CargoResult<(Test, Vec<ProcessError>)> {
    let config = options.compile_opts.config;
    let cwd = options.compile_opts.config.cwd();

    let mut errors = Vec::new();

    for &(ref pkg, ref kind, ref test, ref exe) in &compilation.tests {
        let to_display = match util::without_prefix(exe, cwd) {
            Some(path) => path,
            None => &**exe,
        };
        let mut cmd = compilation.target_process(exe, pkg)?;
        cmd.args(test_args);
        config.shell().concise(|shell| {
            shell.status("Running", to_display.display().to_string())
        })?;
        config.shell().verbose(|shell| {
            shell.status("Running", cmd.to_string())
        })?;

        let result = cmd.exec();

        match result {
            Err(e) => {
                let e = e.downcast::<ProcessError>()?;
                errors.push((kind.clone(), test.clone(), e));
                if !options.no_fail_fast {
                    break;
                }
            }
            Ok(()) => {}
        }
    }

    if errors.len() == 1 {
        let (kind, test, e) = errors.pop().unwrap();
        Ok((Test::UnitTest(kind, test), vec![e]))
    } else {
        Ok((Test::Multiple, errors.into_iter().map((|(_, _, e)| e)).collect()))
    }
}

fn run_doc_tests(options: &TestOptions,
                 test_args: &[String],
                 compilation: &Compilation)
                 -> CargoResult<(Test, Vec<ProcessError>)> {
    let mut errors = Vec::new();
    let config = options.compile_opts.config;

    // We don't build/rust doctests if target != host
    if config.rustc()?.host != compilation.target {
        return Ok((Test::Doc, errors));
    }

    let libs = compilation.to_doc_test.iter().map(|package| {
        (package, package.targets().iter().filter(|t| t.doctested())
                         .map(|t| (t.src_path(), t.name(), t.crate_name())))
    });

    for (package, tests) in libs {
        for (lib, name, crate_name) in tests {
            config.shell().status("Doc-tests", name)?;
            let mut p = compilation.rustdoc_process(package)?;
            p.arg("--test").arg(lib)
             .arg("--crate-name").arg(&crate_name);

            for &rust_dep in &[&compilation.deps_output] {
                let mut arg = OsString::from("dependency=");
                arg.push(rust_dep);
                p.arg("-L").arg(arg);
            }

            for native_dep in compilation.native_dirs.iter() {
                p.arg("-L").arg(native_dep);
            }

            for &host_rust_dep in &[&compilation.host_deps_output] {
                let mut arg = OsString::from("dependency=");
                arg.push(host_rust_dep);
                p.arg("-L").arg(arg);
            }

            for arg in test_args {
                p.arg("--test-args").arg(arg);
            }

            if let Some(cfgs) = compilation.cfgs.get(package.package_id()) {
                for cfg in cfgs.iter() {
                    p.arg("--cfg").arg(cfg);
                }
            }

            let libs = &compilation.libraries[package.package_id()];
            for &(ref target, ref lib) in libs.iter() {
                // Note that we can *only* doctest rlib outputs here.  A
                // staticlib output cannot be linked by the compiler (it just
                // doesn't do that). A dylib output, however, can be linked by
                // the compiler, but will always fail. Currently all dylibs are
                // built as "static dylibs" where the standard library is
                // statically linked into the dylib. The doc tests fail,
                // however, for now as they try to link the standard library
                // dynamically as well, causing problems. As a result we only
                // pass `--extern` for rlib deps and skip out on all other
                // artifacts.
                if lib.extension() != Some(OsStr::new("rlib")) &&
                   !target.for_host() {
                    continue
                }
                let mut arg = OsString::from(target.crate_name());
                arg.push("=");
                arg.push(lib);
                p.arg("--extern").arg(&arg);
            }

            if let Some(flags) = compilation.rustdocflags.get(package.package_id()) {
                p.args(flags);
            }

            config.shell().verbose(|shell| {
                shell.status("Running", p.to_string())
            })?;
            if let Err(e) = p.exec() {
                let e = e.downcast::<ProcessError>()?;
                errors.push(e);
                if !options.no_fail_fast {
                    return Ok((Test::Doc, errors));
                }
            }
        }
    }
    Ok((Test::Doc, errors))
}
<commit_msg>Auto merge of #4909 - zackmdavis:tidy_paren, r=matklad<commit_after>use std::ffi::{OsString, OsStr};

use ops::{self, Compilation};
use util::{self, CargoTestError, Test, ProcessError};
use util::errors::CargoResult;
use core::Workspace;

pub struct TestOptions<'a> {
    pub compile_opts: ops::CompileOptions<'a>,
    pub no_run: bool,
    pub no_fail_fast: bool,
    pub only_doc: bool,
}

pub fn run_tests(ws: &Workspace,
                 options: &TestOptions,
                 test_args: &[String]) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        return Ok(None)
    }
    let (test, mut errors) = if options.only_doc {
        assert!(options.compile_opts.filter.is_specific());
        run_doc_tests(options, test_args, &compilation)?
    } else {
        run_unit_tests(options, test_args, &compilation)?
    };

    // If we have an error and want to fail fast, return
    if !errors.is_empty() && !options.no_fail_fast {
        return Ok(Some(CargoTestError::new(test, errors)))
    }

    // If a specific test was requested or we're not running any tests at all,
    // don't run any doc tests.
    if options.compile_opts.filter.is_specific() {
        match errors.len() {
            0 => return Ok(None),
            _ => return Ok(Some(CargoTestError::new(test, errors)))
        }
    }

    let (doctest, docerrors) = run_doc_tests(options, test_args, &compilation)?;
    let test = if docerrors.is_empty() { test } else { doctest };
    errors.extend(docerrors);
    if errors.is_empty() {
        Ok(None)
    } else {
        Ok(Some(CargoTestError::new(test, errors)))
    }
}

pub fn run_benches(ws: &Workspace,
                   options: &TestOptions,
                   args: &[String]) -> CargoResult<Option<CargoTestError>> {
    let mut args = args.to_vec();
    args.push("--bench".to_string());
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        return Ok(None)
    }
    let (test, errors) = run_unit_tests(options, &args, &compilation)?;
    match errors.len() {
        0 => Ok(None),
        _ => Ok(Some(CargoTestError::new(test, errors))),
    }
}

fn compile_tests<'a>(ws: &Workspace<'a>,
                     options: &TestOptions<'a>)
                     -> CargoResult<Compilation<'a>> {
    let mut compilation = ops::compile(ws, &options.compile_opts)?;
    compilation.tests.sort_by(|a, b| {
        (a.0.package_id(), &a.1, &a.2).cmp(&(b.0.package_id(), &b.1, &b.2))
    });
    Ok(compilation)
}

/// Run the unit and integration tests of a project.
fn run_unit_tests(options: &TestOptions,
                  test_args: &[String],
                  compilation: &Compilation)
                  -> CargoResult<(Test, Vec<ProcessError>)> {
    let config = options.compile_opts.config;
    let cwd = options.compile_opts.config.cwd();

    let mut errors = Vec::new();

    for &(ref pkg, ref kind, ref test, ref exe) in &compilation.tests {
        let to_display = match util::without_prefix(exe, cwd) {
            Some(path) => path,
            None => &**exe,
        };
        let mut cmd = compilation.target_process(exe, pkg)?;
        cmd.args(test_args);
        config.shell().concise(|shell| {
            shell.status("Running", to_display.display().to_string())
        })?;
        config.shell().verbose(|shell| {
            shell.status("Running", cmd.to_string())
        })?;

        let result = cmd.exec();

        match result {
            Err(e) => {
                let e = e.downcast::<ProcessError>()?;
                errors.push((kind.clone(), test.clone(), e));
                if !options.no_fail_fast {
                    break;
                }
            }
            Ok(()) => {}
        }
    }

    if errors.len() == 1 {
        let (kind, test, e) = errors.pop().unwrap();
        Ok((Test::UnitTest(kind, test), vec![e]))
    } else {
        Ok((Test::Multiple, errors.into_iter().map(|(_, _, e)| e).collect()))
    }
}

fn run_doc_tests(options: &TestOptions,
                 test_args: &[String],
                 compilation: &Compilation)
                 -> CargoResult<(Test, Vec<ProcessError>)> {
    let mut errors = Vec::new();
    let config = options.compile_opts.config;

    // We don't build/rust doctests if target != host
    if config.rustc()?.host != compilation.target {
        return Ok((Test::Doc, errors));
    }

    let libs = compilation.to_doc_test.iter().map(|package| {
        (package, package.targets().iter().filter(|t| t.doctested())
                         .map(|t| (t.src_path(), t.name(), t.crate_name())))
    });

    for (package, tests) in libs {
        for (lib, name, crate_name) in tests {
            config.shell().status("Doc-tests", name)?;
            let mut p = compilation.rustdoc_process(package)?;
            p.arg("--test").arg(lib)
             .arg("--crate-name").arg(&crate_name);

            for &rust_dep in &[&compilation.deps_output] {
                let mut arg = OsString::from("dependency=");
                arg.push(rust_dep);
                p.arg("-L").arg(arg);
            }

            for native_dep in compilation.native_dirs.iter() {
                p.arg("-L").arg(native_dep);
            }

            for &host_rust_dep in &[&compilation.host_deps_output] {
                let mut arg = OsString::from("dependency=");
                arg.push(host_rust_dep);
                p.arg("-L").arg(arg);
            }

            for arg in test_args {
                p.arg("--test-args").arg(arg);
            }

            if let Some(cfgs) = compilation.cfgs.get(package.package_id()) {
                for cfg in cfgs.iter() {
                    p.arg("--cfg").arg(cfg);
                }
            }

            let libs = &compilation.libraries[package.package_id()];
            for &(ref target, ref lib) in libs.iter() {
                // Note that we can *only* doctest rlib outputs here.  A
                // staticlib output cannot be linked by the compiler (it just
                // doesn't do that). A dylib output, however, can be linked by
                // the compiler, but will always fail. Currently all dylibs are
                // built as "static dylibs" where the standard library is
                // statically linked into the dylib. The doc tests fail,
                // however, for now as they try to link the standard library
                // dynamically as well, causing problems. As a result we only
                // pass `--extern` for rlib deps and skip out on all other
                // artifacts.
                if lib.extension() != Some(OsStr::new("rlib")) &&
                   !target.for_host() {
                    continue
                }
                let mut arg = OsString::from(target.crate_name());
                arg.push("=");
                arg.push(lib);
                p.arg("--extern").arg(&arg);
            }

            if let Some(flags) = compilation.rustdocflags.get(package.package_id()) {
                p.args(flags);
            }

            config.shell().verbose(|shell| {
                shell.status("Running", p.to_string())
            })?;
            if let Err(e) = p.exec() {
                let e = e.downcast::<ProcessError>()?;
                errors.push(e);
                if !options.no_fail_fast {
                    return Ok((Test::Doc, errors));
                }
            }
        }
    }
    Ok((Test::Doc, errors))
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

//! The Gamma and derived distributions.

use core::num::Float;

use {Rng, Open01};
use super::normal::StandardNormal;
use super::{IndependentSample, Sample, Exp};

/// The Gamma distribution `Gamma(shape, scale)` distribution.
///
/// The density function of this distribution is
///
/// ```notrust
/// f(x) =  x^(k - 1) * exp(-x / θ) / (Γ(k) * θ^k)
/// ```
///
/// where `Γ` is the Gamma function, `k` is the shape and `θ` is the
/// scale and both `k` and `θ` are strictly positive.
///
/// The algorithm used is that described by Marsaglia & Tsang 2000[1],
/// falling back to directly sampling from an Exponential for `shape
/// == 1`, and using the boosting technique described in [1] for
/// `shape < 1`.
///
/// # Example
///
/// ```rust
/// use std::rand;
/// use std::rand::distributions::{IndependentSample, Gamma};
///
/// let gamma = Gamma::new(2.0, 5.0);
/// let v = gamma.ind_sample(&mut rand::task_rng());
/// println!("{} is from a Gamma(2, 5) distribution", v);
/// ```
///
/// [1]: George Marsaglia and Wai Wan Tsang. 2000. "A Simple Method
/// for Generating Gamma Variables" *ACM Trans. Math. Softw.* 26, 3
/// (September 2000),
/// 363-372. DOI:[10.1145/358407.358414](http://doi.acm.org/10.1145/358407.358414)
pub struct Gamma {
    repr: GammaRepr,
}

enum GammaRepr {
    Large(GammaLargeShape),
    One(Exp),
    Small(GammaSmallShape)
}

// These two helpers could be made public, but saving the
// match-on-Gamma-enum branch from using them directly (e.g. if one
// knows that the shape is always > 1) doesn't appear to be much
// faster.

/// Gamma distribution where the shape parameter is less than 1.
///
/// Note, samples from this require a compulsory floating-point `pow`
/// call, which makes it significantly slower than sampling from a
/// gamma distribution where the shape parameter is greater than or
/// equal to 1.
///
/// See `Gamma` for sampling from a Gamma distribution with general
/// shape parameters.
struct GammaSmallShape {
    inv_shape: f64,
    large_shape: GammaLargeShape
}

/// Gamma distribution where the shape parameter is larger than 1.
///
/// See `Gamma` for sampling from a Gamma distribution with general
/// shape parameters.
struct GammaLargeShape {
    shape: f64,
    scale: f64,
    c: f64,
    d: f64
}

impl Gamma {
    /// Construct an object representing the `Gamma(shape, scale)`
    /// distribution.
    ///
    /// Fails if `shape <= 0` or `scale <= 0`.
    pub fn new(shape: f64, scale: f64) -> Gamma {
        assert!(shape > 0.0, "Gamma::new called with shape <= 0");
        assert!(scale > 0.0, "Gamma::new called with scale <= 0");

        let repr = match shape {
            1.0        => One(Exp::new(1.0 / scale)),
            0.0 .. 1.0 => Small(GammaSmallShape::new_raw(shape, scale)),
            _          => Large(GammaLargeShape::new_raw(shape, scale))
        };
        Gamma { repr: repr }
    }
}

impl GammaSmallShape {
    fn new_raw(shape: f64, scale: f64) -> GammaSmallShape {
        GammaSmallShape {
            inv_shape: 1. / shape,
            large_shape: GammaLargeShape::new_raw(shape + 1.0, scale)
        }
    }
}

impl GammaLargeShape {
    fn new_raw(shape: f64, scale: f64) -> GammaLargeShape {
        let d = shape - 1. / 3.;
        GammaLargeShape {
            shape: shape,
            scale: scale,
            c: 1. / (9. * d).sqrt(),
            d: d
        }
    }
}

impl Sample<f64> for Gamma {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl Sample<f64> for GammaSmallShape {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl Sample<f64> for GammaLargeShape {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}

impl IndependentSample<f64> for Gamma {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        match self.repr {
            Small(ref g) => g.ind_sample(rng),
            One(ref g) => g.ind_sample(rng),
            Large(ref g) => g.ind_sample(rng),
        }
    }
}
impl IndependentSample<f64> for GammaSmallShape {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        let Open01(u) = rng.gen::<Open01<f64>>();

        self.large_shape.ind_sample(rng) * u.powf(self.inv_shape)
    }
}
impl IndependentSample<f64> for GammaLargeShape {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        loop {
            let StandardNormal(x) = rng.gen::<StandardNormal>();
            let v_cbrt = 1.0 + self.c * x;
            if v_cbrt <= 0.0 { // a^3 <= 0 iff a <= 0
                continue
            }

            let v = v_cbrt * v_cbrt * v_cbrt;
            let Open01(u) = rng.gen::<Open01<f64>>();

            let x_sqr = x * x;
            if u < 1.0 - 0.0331 * x_sqr * x_sqr ||
                u.ln() < 0.5 * x_sqr + self.d * (1.0 - v + v.ln()) {
                return self.d * v * self.scale
            }
        }
    }
}

/// The chi-squared distribution `χ²(k)`, where `k` is the degrees of
/// freedom.
///
/// For `k > 0` integral, this distribution is the sum of the squares
/// of `k` independent standard normal random variables. For other
/// `k`, this uses the equivalent characterisation `χ²(k) = Gamma(k/2,
/// 2)`.
///
/// # Example
///
/// ```rust
/// use std::rand;
/// use std::rand::distributions::{ChiSquared, IndependentSample};
///
/// let chi = ChiSquared::new(11.0);
/// let v = chi.ind_sample(&mut rand::task_rng());
/// println!("{} is from a χ²(11) distribution", v)
/// ```
pub struct ChiSquared {
    repr: ChiSquaredRepr,
}

enum ChiSquaredRepr {
    // k == 1, Gamma(alpha, ..) is particularly slow for alpha < 1,
    // e.g. when alpha = 1/2 as it would be for this case, so special-
    // casing and using the definition of N(0,1)^2 is faster.
    DoFExactlyOne,
    DoFAnythingElse(Gamma),
}

impl ChiSquared {
    /// Create a new chi-squared distribution with degrees-of-freedom
    /// `k`. Fails if `k < 0`.
    pub fn new(k: f64) -> ChiSquared {
        let repr = if k == 1.0 {
            DoFExactlyOne
        } else {
            assert!(k > 0.0, "ChiSquared::new called with `k` < 0");
            DoFAnythingElse(Gamma::new(0.5 * k, 2.0))
        };
        ChiSquared { repr: repr }
    }
}
impl Sample<f64> for ChiSquared {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl IndependentSample<f64> for ChiSquared {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        match self.repr {
            DoFExactlyOne => {
                // k == 1 => N(0,1)^2
                let StandardNormal(norm) = rng.gen::<StandardNormal>();
                norm * norm
            }
            DoFAnythingElse(ref g) => g.ind_sample(rng)
        }
    }
}

/// The Fisher F distribution `F(m, n)`.
///
/// This distribution is equivalent to the ratio of two normalised
/// chi-squared distributions, that is, `F(m,n) = (χ²(m)/m) /
/// (χ²(n)/n)`.
///
/// # Example
///
/// ```rust
/// use std::rand;
/// use std::rand::distributions::{FisherF, IndependentSample};
///
/// let f = FisherF::new(2.0, 32.0);
/// let v = f.ind_sample(&mut rand::task_rng());
/// println!("{} is from an F(2, 32) distribution", v)
/// ```
pub struct FisherF {
    numer: ChiSquared,
    denom: ChiSquared,
    // denom_dof / numer_dof so that this can just be a straight
    // multiplication, rather than a division.
    dof_ratio: f64,
}

impl FisherF {
    /// Create a new `FisherF` distribution, with the given
    /// parameter. Fails if either `m` or `n` are not positive.
    pub fn new(m: f64, n: f64) -> FisherF {
        assert!(m > 0.0, "FisherF::new called with `m < 0`");
        assert!(n > 0.0, "FisherF::new called with `n < 0`");

        FisherF {
            numer: ChiSquared::new(m),
            denom: ChiSquared::new(n),
            dof_ratio: n / m
        }
    }
}
impl Sample<f64> for FisherF {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl IndependentSample<f64> for FisherF {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        self.numer.ind_sample(rng) / self.denom.ind_sample(rng) * self.dof_ratio
    }
}

/// The Student t distribution, `t(nu)`, where `nu` is the degrees of
/// freedom.
///
/// # Example
///
/// ```rust
/// use std::rand;
/// use std::rand::distributions::{StudentT, IndependentSample};
///
/// let t = StudentT::new(11.0);
/// let v = t.ind_sample(&mut rand::task_rng());
/// println!("{} is from a t(11) distribution", v)
/// ```
pub struct StudentT {
    chi: ChiSquared,
    dof: f64
}

impl StudentT {
    /// Create a new Student t distribution with `n` degrees of
    /// freedom. Fails if `n <= 0`.
    pub fn new(n: f64) -> StudentT {
        assert!(n > 0.0, "StudentT::new called with `n <= 0`");
        StudentT {
            chi: ChiSquared::new(n),
            dof: n
        }
    }
}
impl Sample<f64> for StudentT {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl IndependentSample<f64> for StudentT {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        let StandardNormal(norm) = rng.gen::<StandardNormal>();
        norm * (self.dof / self.chi.ind_sample(rng)).sqrt()
    }
}

#[cfg(test)]
mod test {
    use std::prelude::*;

    use distributions::{Sample, IndependentSample};
    use super::{ChiSquared, StudentT, FisherF};

    #[test]
    fn test_chi_squared_one() {
        let mut chi = ChiSquared::new(1.0);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            chi.sample(&mut rng);
            chi.ind_sample(&mut rng);
        }
    }
    #[test]
    fn test_chi_squared_small() {
        let mut chi = ChiSquared::new(0.5);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            chi.sample(&mut rng);
            chi.ind_sample(&mut rng);
        }
    }
    #[test]
    fn test_chi_squared_large() {
        let mut chi = ChiSquared::new(30.0);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            chi.sample(&mut rng);
            chi.ind_sample(&mut rng);
        }
    }
    #[test]
    #[should_fail]
    fn test_chi_squared_invalid_dof() {
        ChiSquared::new(-1.0);
    }

    #[test]
    fn test_f() {
        let mut f = FisherF::new(2.0, 32.0);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            f.sample(&mut rng);
            f.ind_sample(&mut rng);
        }
    }

    #[test]
    fn test_t() {
        let mut t = StudentT::new(11.0);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            t.sample(&mut rng);
            t.ind_sample(&mut rng);
        }
    }
}

#[cfg(test)]
mod bench {
    extern crate test;
    use std::prelude::*;
    use self::test::Bencher;
    use std::mem::size_of;
    use distributions::IndependentSample;
    use super::Gamma;

    #[bench]
    fn bench_gamma_large_shape(b: &mut Bencher) {
        let gamma = Gamma::new(10., 1.0);
        let mut rng = ::test::weak_rng();

        b.iter(|| {
            for _ in range(0, ::RAND_BENCH_N) {
                gamma.ind_sample(&mut rng);
            }
        });
        b.bytes = size_of::<f64>() as u64 * ::RAND_BENCH_N;
    }

    #[bench]
    fn bench_gamma_small_shape(b: &mut Bencher) {
        let gamma = Gamma::new(0.1, 1.0);
        let mut rng = ::test::weak_rng();

        b.iter(|| {
            for _ in range(0, ::RAND_BENCH_N) {
                gamma.ind_sample(&mut rng);
            }
        });
        b.bytes = size_of::<f64>() as u64 * ::RAND_BENCH_N;
    }
}
<commit_msg>docs: Stop using `notrust`<commit_after>// Copyright 2013 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.

//! The Gamma and derived distributions.

use core::num::Float;

use {Rng, Open01};
use super::normal::StandardNormal;
use super::{IndependentSample, Sample, Exp};

/// The Gamma distribution `Gamma(shape, scale)` distribution.
///
/// The density function of this distribution is
///
/// ```text
/// f(x) =  x^(k - 1) * exp(-x / θ) / (Γ(k) * θ^k)
/// ```
///
/// where `Γ` is the Gamma function, `k` is the shape and `θ` is the
/// scale and both `k` and `θ` are strictly positive.
///
/// The algorithm used is that described by Marsaglia & Tsang 2000[1],
/// falling back to directly sampling from an Exponential for `shape
/// == 1`, and using the boosting technique described in [1] for
/// `shape < 1`.
///
/// # Example
///
/// ```rust
/// use std::rand;
/// use std::rand::distributions::{IndependentSample, Gamma};
///
/// let gamma = Gamma::new(2.0, 5.0);
/// let v = gamma.ind_sample(&mut rand::task_rng());
/// println!("{} is from a Gamma(2, 5) distribution", v);
/// ```
///
/// [1]: George Marsaglia and Wai Wan Tsang. 2000. "A Simple Method
/// for Generating Gamma Variables" *ACM Trans. Math. Softw.* 26, 3
/// (September 2000),
/// 363-372. DOI:[10.1145/358407.358414](http://doi.acm.org/10.1145/358407.358414)
pub struct Gamma {
    repr: GammaRepr,
}

enum GammaRepr {
    Large(GammaLargeShape),
    One(Exp),
    Small(GammaSmallShape)
}

// These two helpers could be made public, but saving the
// match-on-Gamma-enum branch from using them directly (e.g. if one
// knows that the shape is always > 1) doesn't appear to be much
// faster.

/// Gamma distribution where the shape parameter is less than 1.
///
/// Note, samples from this require a compulsory floating-point `pow`
/// call, which makes it significantly slower than sampling from a
/// gamma distribution where the shape parameter is greater than or
/// equal to 1.
///
/// See `Gamma` for sampling from a Gamma distribution with general
/// shape parameters.
struct GammaSmallShape {
    inv_shape: f64,
    large_shape: GammaLargeShape
}

/// Gamma distribution where the shape parameter is larger than 1.
///
/// See `Gamma` for sampling from a Gamma distribution with general
/// shape parameters.
struct GammaLargeShape {
    shape: f64,
    scale: f64,
    c: f64,
    d: f64
}

impl Gamma {
    /// Construct an object representing the `Gamma(shape, scale)`
    /// distribution.
    ///
    /// Fails if `shape <= 0` or `scale <= 0`.
    pub fn new(shape: f64, scale: f64) -> Gamma {
        assert!(shape > 0.0, "Gamma::new called with shape <= 0");
        assert!(scale > 0.0, "Gamma::new called with scale <= 0");

        let repr = match shape {
            1.0        => One(Exp::new(1.0 / scale)),
            0.0 .. 1.0 => Small(GammaSmallShape::new_raw(shape, scale)),
            _          => Large(GammaLargeShape::new_raw(shape, scale))
        };
        Gamma { repr: repr }
    }
}

impl GammaSmallShape {
    fn new_raw(shape: f64, scale: f64) -> GammaSmallShape {
        GammaSmallShape {
            inv_shape: 1. / shape,
            large_shape: GammaLargeShape::new_raw(shape + 1.0, scale)
        }
    }
}

impl GammaLargeShape {
    fn new_raw(shape: f64, scale: f64) -> GammaLargeShape {
        let d = shape - 1. / 3.;
        GammaLargeShape {
            shape: shape,
            scale: scale,
            c: 1. / (9. * d).sqrt(),
            d: d
        }
    }
}

impl Sample<f64> for Gamma {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl Sample<f64> for GammaSmallShape {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl Sample<f64> for GammaLargeShape {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}

impl IndependentSample<f64> for Gamma {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        match self.repr {
            Small(ref g) => g.ind_sample(rng),
            One(ref g) => g.ind_sample(rng),
            Large(ref g) => g.ind_sample(rng),
        }
    }
}
impl IndependentSample<f64> for GammaSmallShape {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        let Open01(u) = rng.gen::<Open01<f64>>();

        self.large_shape.ind_sample(rng) * u.powf(self.inv_shape)
    }
}
impl IndependentSample<f64> for GammaLargeShape {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        loop {
            let StandardNormal(x) = rng.gen::<StandardNormal>();
            let v_cbrt = 1.0 + self.c * x;
            if v_cbrt <= 0.0 { // a^3 <= 0 iff a <= 0
                continue
            }

            let v = v_cbrt * v_cbrt * v_cbrt;
            let Open01(u) = rng.gen::<Open01<f64>>();

            let x_sqr = x * x;
            if u < 1.0 - 0.0331 * x_sqr * x_sqr ||
                u.ln() < 0.5 * x_sqr + self.d * (1.0 - v + v.ln()) {
                return self.d * v * self.scale
            }
        }
    }
}

/// The chi-squared distribution `χ²(k)`, where `k` is the degrees of
/// freedom.
///
/// For `k > 0` integral, this distribution is the sum of the squares
/// of `k` independent standard normal random variables. For other
/// `k`, this uses the equivalent characterisation `χ²(k) = Gamma(k/2,
/// 2)`.
///
/// # Example
///
/// ```rust
/// use std::rand;
/// use std::rand::distributions::{ChiSquared, IndependentSample};
///
/// let chi = ChiSquared::new(11.0);
/// let v = chi.ind_sample(&mut rand::task_rng());
/// println!("{} is from a χ²(11) distribution", v)
/// ```
pub struct ChiSquared {
    repr: ChiSquaredRepr,
}

enum ChiSquaredRepr {
    // k == 1, Gamma(alpha, ..) is particularly slow for alpha < 1,
    // e.g. when alpha = 1/2 as it would be for this case, so special-
    // casing and using the definition of N(0,1)^2 is faster.
    DoFExactlyOne,
    DoFAnythingElse(Gamma),
}

impl ChiSquared {
    /// Create a new chi-squared distribution with degrees-of-freedom
    /// `k`. Fails if `k < 0`.
    pub fn new(k: f64) -> ChiSquared {
        let repr = if k == 1.0 {
            DoFExactlyOne
        } else {
            assert!(k > 0.0, "ChiSquared::new called with `k` < 0");
            DoFAnythingElse(Gamma::new(0.5 * k, 2.0))
        };
        ChiSquared { repr: repr }
    }
}
impl Sample<f64> for ChiSquared {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl IndependentSample<f64> for ChiSquared {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        match self.repr {
            DoFExactlyOne => {
                // k == 1 => N(0,1)^2
                let StandardNormal(norm) = rng.gen::<StandardNormal>();
                norm * norm
            }
            DoFAnythingElse(ref g) => g.ind_sample(rng)
        }
    }
}

/// The Fisher F distribution `F(m, n)`.
///
/// This distribution is equivalent to the ratio of two normalised
/// chi-squared distributions, that is, `F(m,n) = (χ²(m)/m) /
/// (χ²(n)/n)`.
///
/// # Example
///
/// ```rust
/// use std::rand;
/// use std::rand::distributions::{FisherF, IndependentSample};
///
/// let f = FisherF::new(2.0, 32.0);
/// let v = f.ind_sample(&mut rand::task_rng());
/// println!("{} is from an F(2, 32) distribution", v)
/// ```
pub struct FisherF {
    numer: ChiSquared,
    denom: ChiSquared,
    // denom_dof / numer_dof so that this can just be a straight
    // multiplication, rather than a division.
    dof_ratio: f64,
}

impl FisherF {
    /// Create a new `FisherF` distribution, with the given
    /// parameter. Fails if either `m` or `n` are not positive.
    pub fn new(m: f64, n: f64) -> FisherF {
        assert!(m > 0.0, "FisherF::new called with `m < 0`");
        assert!(n > 0.0, "FisherF::new called with `n < 0`");

        FisherF {
            numer: ChiSquared::new(m),
            denom: ChiSquared::new(n),
            dof_ratio: n / m
        }
    }
}
impl Sample<f64> for FisherF {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl IndependentSample<f64> for FisherF {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        self.numer.ind_sample(rng) / self.denom.ind_sample(rng) * self.dof_ratio
    }
}

/// The Student t distribution, `t(nu)`, where `nu` is the degrees of
/// freedom.
///
/// # Example
///
/// ```rust
/// use std::rand;
/// use std::rand::distributions::{StudentT, IndependentSample};
///
/// let t = StudentT::new(11.0);
/// let v = t.ind_sample(&mut rand::task_rng());
/// println!("{} is from a t(11) distribution", v)
/// ```
pub struct StudentT {
    chi: ChiSquared,
    dof: f64
}

impl StudentT {
    /// Create a new Student t distribution with `n` degrees of
    /// freedom. Fails if `n <= 0`.
    pub fn new(n: f64) -> StudentT {
        assert!(n > 0.0, "StudentT::new called with `n <= 0`");
        StudentT {
            chi: ChiSquared::new(n),
            dof: n
        }
    }
}
impl Sample<f64> for StudentT {
    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
}
impl IndependentSample<f64> for StudentT {
    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
        let StandardNormal(norm) = rng.gen::<StandardNormal>();
        norm * (self.dof / self.chi.ind_sample(rng)).sqrt()
    }
}

#[cfg(test)]
mod test {
    use std::prelude::*;

    use distributions::{Sample, IndependentSample};
    use super::{ChiSquared, StudentT, FisherF};

    #[test]
    fn test_chi_squared_one() {
        let mut chi = ChiSquared::new(1.0);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            chi.sample(&mut rng);
            chi.ind_sample(&mut rng);
        }
    }
    #[test]
    fn test_chi_squared_small() {
        let mut chi = ChiSquared::new(0.5);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            chi.sample(&mut rng);
            chi.ind_sample(&mut rng);
        }
    }
    #[test]
    fn test_chi_squared_large() {
        let mut chi = ChiSquared::new(30.0);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            chi.sample(&mut rng);
            chi.ind_sample(&mut rng);
        }
    }
    #[test]
    #[should_fail]
    fn test_chi_squared_invalid_dof() {
        ChiSquared::new(-1.0);
    }

    #[test]
    fn test_f() {
        let mut f = FisherF::new(2.0, 32.0);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            f.sample(&mut rng);
            f.ind_sample(&mut rng);
        }
    }

    #[test]
    fn test_t() {
        let mut t = StudentT::new(11.0);
        let mut rng = ::test::rng();
        for _ in range(0, 1000) {
            t.sample(&mut rng);
            t.ind_sample(&mut rng);
        }
    }
}

#[cfg(test)]
mod bench {
    extern crate test;
    use std::prelude::*;
    use self::test::Bencher;
    use std::mem::size_of;
    use distributions::IndependentSample;
    use super::Gamma;

    #[bench]
    fn bench_gamma_large_shape(b: &mut Bencher) {
        let gamma = Gamma::new(10., 1.0);
        let mut rng = ::test::weak_rng();

        b.iter(|| {
            for _ in range(0, ::RAND_BENCH_N) {
                gamma.ind_sample(&mut rng);
            }
        });
        b.bytes = size_of::<f64>() as u64 * ::RAND_BENCH_N;
    }

    #[bench]
    fn bench_gamma_small_shape(b: &mut Bencher) {
        let gamma = Gamma::new(0.1, 1.0);
        let mut rng = ::test::weak_rng();

        b.iter(|| {
            for _ in range(0, ::RAND_BENCH_N) {
                gamma.ind_sample(&mut rng);
            }
        });
        b.bytes = size_of::<f64>() as u64 * ::RAND_BENCH_N;
    }
}
<|endoftext|>


<commit_before>use std::fmt::{self, Display};
use std::time::{SystemTime, UNIX_EPOCH};

const MINUTE_SECONDS: u64 = 60;
const HOUR_SECONDS: u64 = 60 * MINUTE_SECONDS;
const DAY_SECONDS: u64 = 24 * HOUR_SECONDS;

#[derive(Copy, Clone)]
pub struct HumanTime(u64);

impl HumanTime {
    pub fn new(secs: u64) -> Self {
        HumanTime(secs)
    }

    pub fn days(self) -> Option<u64> {
        if self.0 >= DAY_SECONDS {
            Some(self.0 / DAY_SECONDS)
        } else {
            None
        }
    }

    pub fn hours(self) -> Option<u64> {
        if self.0 >= HOUR_SECONDS {
            Some(self.0 / HOUR_SECONDS % 24)
        } else {
            None
        }
    }

    pub fn minutes(self) -> Option<u64> {
        if self.0 >= MINUTE_SECONDS {
            Some(self.0 / MINUTE_SECONDS % 60)
        } else {
            None
        }
    }

    pub fn secs(self) -> u64 {
        self.0 % 60
    }

    pub fn to_human(&self, max_parts: usize) -> String {
        self.to_parts()
            .into_iter()
            .take(max_parts)
            .collect::<Vec<_>>()
            .join(" ")
    }

    pub fn to_parts(&self) -> Vec<String> {
        let mut result = Vec::with_capacity(4);
        if let Some(d) = self.days() {
            result.push(format!("{}d", d));
        }
        if let Some(h) = self.hours() {
            result.push(format!("{}h", h));
        }
        if let Some(m) = self.minutes() {
            result.push(format!("{}m", m));
        }
        result.push(format!("{}s", self.secs()));
        result
    }
}

impl Display for HumanTime {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_parts().join(" "))
    }
}

pub fn seconds_since_build() -> Option<u64> {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .ok()
        .and_then(|x| x.as_secs().checked_sub(get_build_epoch_seconds()))
}

fn get_build_epoch_seconds() -> u64 {
    env!("EPOCHSECONDS")
        .parse()
        .expect("Unable to get build time")
}

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

    #[test]
    fn test() {
        assert_eq!(HumanTime(1).to_human(2), "1s");
        assert_eq!(HumanTime(10).to_human(2), "10s");
        assert_eq!(HumanTime(59).to_human(2), "59s");
        assert_eq!(HumanTime(MINUTE_SECONDS).to_human(2), "1m 0s");
        assert_eq!(HumanTime(HOUR_SECONDS).to_human(2), "1h 0m");
        assert_eq!(HumanTime(HOUR_SECONDS - 1).to_human(2), "59m 59s");
        assert_eq!(HumanTime(HOUR_SECONDS).to_human(2), "1h 0m");
        assert_eq!(HumanTime(HOUR_SECONDS + 1).to_human(2), "1h 0m");
        assert_eq!(HumanTime(DAY_SECONDS - 1).to_human(2), "23h 59m");
        assert_eq!(HumanTime(DAY_SECONDS).to_human(2), "1d 0h");
        assert_eq!(HumanTime(DAY_SECONDS + 1).to_human(2), "1d 0h");
    }
}
<commit_msg>Use vec truncate<commit_after>use std::fmt::{self, Display};
use std::time::{SystemTime, UNIX_EPOCH};

const MINUTE_SECONDS: u64 = 60;
const HOUR_SECONDS: u64 = 60 * MINUTE_SECONDS;
const DAY_SECONDS: u64 = 24 * HOUR_SECONDS;

#[derive(Copy, Clone)]
pub struct HumanTime(u64);

impl HumanTime {
    pub fn new(secs: u64) -> Self {
        HumanTime(secs)
    }

    pub fn days(self) -> Option<u64> {
        if self.0 >= DAY_SECONDS {
            Some(self.0 / DAY_SECONDS)
        } else {
            None
        }
    }

    pub fn hours(self) -> Option<u64> {
        if self.0 >= HOUR_SECONDS {
            Some(self.0 / HOUR_SECONDS % 24)
        } else {
            None
        }
    }

    pub fn minutes(self) -> Option<u64> {
        if self.0 >= MINUTE_SECONDS {
            Some(self.0 / MINUTE_SECONDS % 60)
        } else {
            None
        }
    }

    pub fn secs(self) -> u64 {
        self.0 % 60
    }

    pub fn to_human(&self, max_parts: usize) -> String {
        let mut parts = self.to_parts();
        parts.truncate(max_parts);
        parts.join(" ")
    }

    pub fn to_parts(&self) -> Vec<String> {
        let mut result = Vec::with_capacity(4);
        if let Some(d) = self.days() {
            result.push(format!("{}d", d));
        }
        if let Some(h) = self.hours() {
            result.push(format!("{}h", h));
        }
        if let Some(m) = self.minutes() {
            result.push(format!("{}m", m));
        }
        result.push(format!("{}s", self.secs()));
        result
    }
}

impl Display for HumanTime {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_parts().join(" "))
    }
}

pub fn seconds_since_build() -> Option<u64> {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .ok()
        .and_then(|x| x.as_secs().checked_sub(get_build_epoch_seconds()))
}

fn get_build_epoch_seconds() -> u64 {
    env!("EPOCHSECONDS")
        .parse()
        .expect("Unable to get build time")
}

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

    #[test]
    fn test() {
        assert_eq!(HumanTime(1).to_human(2), "1s");
        assert_eq!(HumanTime(10).to_human(2), "10s");
        assert_eq!(HumanTime(59).to_human(2), "59s");
        assert_eq!(HumanTime(MINUTE_SECONDS).to_human(2), "1m 0s");
        assert_eq!(HumanTime(HOUR_SECONDS).to_human(2), "1h 0m");
        assert_eq!(HumanTime(HOUR_SECONDS - 1).to_human(2), "59m 59s");
        assert_eq!(HumanTime(HOUR_SECONDS).to_human(2), "1h 0m");
        assert_eq!(HumanTime(HOUR_SECONDS + 1).to_human(2), "1h 0m");
        assert_eq!(HumanTime(DAY_SECONDS - 1).to_human(2), "23h 59m");
        assert_eq!(HumanTime(DAY_SECONDS).to_human(2), "1d 0h");
        assert_eq!(HumanTime(DAY_SECONDS + 1).to_human(2), "1d 0h");
    }
}
<|endoftext|>


<commit_before>use std::mem;

#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
struct Point {
    x: f64,
    y: f64,
}

#[allow(dead_code)]
struct Rectangle{
    p1: Point,
    p2: Point,
}

fn origin() -> Point {
    Point{x: 0.0, y: 0.0}
}

fn boxed_origin() -> Box<Point> {
    Box::new(Point {x: 0.0, y: 0.0})
}

fn main () {

    //Stack allocated variable
    let point: Point = origin();
    let rectangle: Rectangle = Rectangle {
        p1: origin(),
        p2: Point {x: 3.0, y: 4.0}
    };
    //Heap allocated rectangle
    let boxed_rectangle: Box<Rectangle> = Box::New(Rectangle {
        p1: origin(),
        p2: origin()
    });

    let boxed_point = Box<Point> = Box::new(origin());

    let box_in_a_box: Box<Box<Point>> = Box::new(boxed_origin());

    println!("Point occupied {} bytes in the stack",
             mem::size_of_val(&point));

    println!("Rectangle occupied {} bytes in the stack",
             mem::size_of_val(&rectangle));

    println!("Boxed point occupied {} bytes in the stack",
             mem::size_of_val(&boxed_point));

    println!("Boxed rectangle occupied {} bytes in the stack",
             mem::size_of_val(&boxed_rectangle));

    println!("Boxed box occupied {} bytes in the stack",
             mem::size_of_val(&box_in_a_box));

    let unboxed_point: Point = *boxed_point;

    println!("Unboixed point occupie {} bytes in the stack", mem::size_of_val(&unboxed_point));
}
<commit_msg>Fixed error in box example<commit_after>use std::mem;

#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
struct Point {
    x: f64,
    y: f64,
}

#[allow(dead_code)]
struct Rectangle{
    p1: Point,
    p2: Point,
}

fn origin() -> Point {
    Point{x: 0.0, y: 0.0}
}

fn boxed_origin() -> Box<Point> {
    Box::new(Point {x: 0.0, y: 0.0})
}

fn main () {

    //Stack allocated variable
    let point: Point = origin();
    let rectangle: Rectangle = Rectangle {
        p1: origin(),
        p2: Point {x: 3.0, y: 4.0}
    };
    //Heap allocated rectangle
    let boxed_rectangle: Box<Rectangle> = Box::new(Rectangle {
        p1: origin(),
        p2: origin()
    });

    let boxed_point:  Box<Point> = Box::new(origin());

    let box_in_a_box: Box<Box<Point>> = Box::new(boxed_origin());

    println!("Point occupied {} bytes in the stack",
             mem::size_of_val(&point));

    println!("Rectangle occupied {} bytes in the stack",
             mem::size_of_val(&rectangle));

    println!("Boxed point occupied {} bytes in the stack",
             mem::size_of_val(&boxed_point));

    println!("Boxed rectangle occupied {} bytes in the stack",
             mem::size_of_val(&boxed_rectangle));

    println!("Boxed box occupied {} bytes in the stack",
             mem::size_of_val(&box_in_a_box));

    let unboxed_point: Point = *boxed_point;

    println!("Unboixed point occupie {} bytes in the stack", mem::size_of_val(&unboxed_point));
}
<|endoftext|>


<commit_before>use rustc_serialize::json::Json;

use client::WeChatClient;
use errors::WeChatError;

#[derive(Debug, Clone)]
pub struct WeChatMenu<'a> {
    client: &'a WeChatClient,
}

impl<'a> WeChatMenu<'a> {

    #[inline]
    pub fn new(client: &'a WeChatClient) -> WeChatMenu<'a> {
        WeChatMenu {
            client: client,
        }
    }

    pub fn create(&self, data: &Json) -> Result<Json, WeChatError> {
        self.client.post("menu/create", vec![], data.as_object().unwrap())
    }

    pub fn get(&self) -> Result<Json, WeChatError> {
        self.client.get("menu/get", vec![])
    }

    pub fn delete(&self) -> Result<Json, WeChatError> {
        self.client.get("menu/delete", vec![])
    }

    pub fn update(&self, data: &Json) -> Result<Json, WeChatError> {
        try!(self.delete());
        self.create(data)
    }

    pub fn get_menu_info(&self) -> Result<Json, WeChatError> {
        self.client.get("get_current_selfmenu_info", vec![])
    }
}<commit_msg>Update menu APIs to use Encodable instead of Json<commit_after>use rustc_serialize::Encodable;
use rustc_serialize::json::Json;

use client::WeChatClient;
use errors::WeChatError;

#[derive(Debug, Clone)]
pub struct WeChatMenu<'a> {
    client: &'a WeChatClient,
}

impl<'a> WeChatMenu<'a> {

    #[inline]
    pub fn new(client: &'a WeChatClient) -> WeChatMenu<'a> {
        WeChatMenu {
            client: client,
        }
    }

    pub fn create<D: Encodable>(&self, data: &D) -> Result<(), WeChatError> {
        try!(self.client.post("menu/create", vec![], data));
        Ok(())
    }

    pub fn get(&self) -> Result<Json, WeChatError> {
        self.client.get("menu/get", vec![])
    }

    pub fn delete(&self) -> Result<(), WeChatError> {
        try!(self.client.get("menu/delete", vec![]));
        Ok(())
    }

    pub fn update<D: Encodable>(&self, data: &D) -> Result<(), WeChatError> {
        try!(self.delete());
        self.create(data)
    }

    pub fn get_menu_info(&self) -> Result<Json, WeChatError> {
        self.client.get("get_current_selfmenu_info", vec![])
    }
}
<|endoftext|>


<commit_before>use envelope;
use pitch;

/// Types for generating the frequency given some playhead position.
pub trait Frequency {
    /// Return the frequency given some playhead percentage through the duration of the Synth.
    /// - 0.0 < perc < 1.0l
    fn hz_at_playhead(&self, perc: f64) -> f64;
    /// Return the frequency as a percentage.
    #[inline]
    fn freq_perc_at_playhead(&self, perc: f64) -> f64 {
        pitch::Hz(self.hz_at_playhead(perc) as f32).perc()
    }
}

/// Alias for the Envelope used.
pub type Envelope = envelope::Envelope;

/// A type that allows dynamically switching between constant and enveloped frequency.
#[derive(Debug, Clone, PartialEq)]
pub enum Dynamic {
    Envelope(Envelope),
    Hz(f64),
}

impl Dynamic {

    /// Return whether or not the Dynamic is an Envelope.
    pub fn is_env(&self) -> bool {
        if let Dynamic::Envelope(_) = *self { true } else { false }
    }

    /// Convert the dynamic to its Envelope variant.
    pub fn to_env(&self) -> Dynamic {
        use std::iter::once;
        if let Dynamic::Hz(hz) = *self {
            let perc = pitch::Hz(hz as f32).perc();
            return Dynamic::Envelope({
                once(envelope::Point::new(0.0, perc, 0.0))
                    .chain(once(envelope::Point::new(1.0, perc, 0.0)))
                    .collect()
            })
        }
        self.clone()
    }

    /// Convert the dynamic to its Hz variant.
    pub fn to_hz(&self) -> Dynamic {
        if let Dynamic::Envelope(ref env) = *self {
            use pitch::{LetterOctave, Letter};
            // Just convert the first point to the constant Hz.
            return match env.points.iter().nth(0) {
                Some(point) => Dynamic::Hz(pitch::Perc(point.y).hz() as f64),
                None => Dynamic::Hz(LetterOctave(Letter::C, 1).hz() as f64),
            }
        }
        self.clone()
    }
}

impl Frequency for f64 {
    #[inline]
    fn hz_at_playhead(&self, _perc: f64) -> f64 { *self }
}

impl Frequency for Envelope {
    #[inline]
    fn hz_at_playhead(&self, perc: f64) -> f64 {
        pitch::Perc(self.freq_perc_at_playhead(perc)).hz() as f64
    }
    #[inline]
    fn freq_perc_at_playhead(&self, perc: f64) -> f64 {
        envelope::Trait::y(self, perc)
            .expect("The given playhead position is out of range (0.0..1.0).")
    }
}

impl Frequency for Dynamic {
    #[inline]
    fn hz_at_playhead(&self, perc: f64) -> f64 {
        match *self {
            Dynamic::Envelope(ref env) => env.hz_at_playhead(perc),
            Dynamic::Hz(hz) => hz,
        }
    }
}

<commit_msg>Remove redundant whitespace.<commit_after>use envelope;
use pitch;

/// Types for generating the frequency given some playhead position.
pub trait Frequency {
    /// Return the frequency given some playhead percentage through the duration of the Synth.
    /// - 0.0 < perc < 1.0l
    fn hz_at_playhead(&self, perc: f64) -> f64;
    /// Return the frequency as a percentage.
    #[inline]
    fn freq_perc_at_playhead(&self, perc: f64) -> f64 {
        pitch::Hz(self.hz_at_playhead(perc) as f32).perc()
    }
}

/// Alias for the Envelope used.
pub type Envelope = envelope::Envelope;

/// A type that allows dynamically switching between constant and enveloped frequency.
#[derive(Debug, Clone, PartialEq)]
pub enum Dynamic {
    Envelope(Envelope),
    Hz(f64),
}

impl Dynamic {

    /// Return whether or not the Dynamic is an Envelope.
    pub fn is_env(&self) -> bool {
        if let Dynamic::Envelope(_) = *self { true } else { false }
    }

    /// Convert the dynamic to its Envelope variant.
    pub fn to_env(&self) -> Dynamic {
        use std::iter::once;
        if let Dynamic::Hz(hz) = *self {
            let perc = pitch::Hz(hz as f32).perc();
            return Dynamic::Envelope({
                once(envelope::Point::new(0.0, perc, 0.0))
                    .chain(once(envelope::Point::new(1.0, perc, 0.0)))
                    .collect()
            })
        }
        self.clone()
    }

    /// Convert the dynamic to its Hz variant.
    pub fn to_hz(&self) -> Dynamic {
        if let Dynamic::Envelope(ref env) = *self {
            use pitch::{LetterOctave, Letter};
            // Just convert the first point to the constant Hz.
            return match env.points.iter().nth(0) {
                Some(point) => Dynamic::Hz(pitch::Perc(point.y).hz() as f64),
                None => Dynamic::Hz(LetterOctave(Letter::C, 1).hz() as f64),
            }
        }
        self.clone()
    }
}

impl Frequency for f64 {
    #[inline]
    fn hz_at_playhead(&self, _perc: f64) -> f64 { *self }
}

impl Frequency for Envelope {
    #[inline]
    fn hz_at_playhead(&self, perc: f64) -> f64 {
        pitch::Perc(self.freq_perc_at_playhead(perc)).hz() as f64
    }
    #[inline]
    fn freq_perc_at_playhead(&self, perc: f64) -> f64 {
        envelope::Trait::y(self, perc)
            .expect("The given playhead position is out of range (0.0..1.0).")
    }
}

impl Frequency for Dynamic {
    #[inline]
    fn hz_at_playhead(&self, perc: f64) -> f64 {
        match *self {
            Dynamic::Envelope(ref env) => env.hz_at_playhead(perc),
            Dynamic::Hz(hz) => hz,
        }
    }
}
<|endoftext|>


<commit_before>use seed::prelude::*;
use crate::Msg;

mod fermat_utils {
    use seed::prelude::*;
    use crate::Msg;

    pub fn save_as_file(filename:String, filecontent:String) -> seed::dom_types::Node<Msg> {
        let href:String = vec!["data:text/plain;",&filecontent].into_iter().collect();
        a![attrs!{At::Download => &filename, At::Href => &href}, "TXT"]
    }
}

pub fn render() -> seed::dom_types::Node<Msg> {

    div![
        h1!["The Fermat Numbers"],
        br![],
        br![],
        br![],
        //todo: replace with rust elements
        El::from_html("

 	No.	Fermat	Digits	Prime / Factors	Download

 	11	22048+1		P27567 × 974849 × 167988556341760475137 × 3560841906445833920513 × P564

 	10	21024+1		45592577 × 6487031809 × 4659775785220018543264560743076778192897 × P252

 	9	2512+1	155	2424833 × 7455602825647884208337395736200454918783366342657 × P99	TXT

 	8	2256+1	78	1238926361552897 × P62	TXT

 	7	2128+1	39	59649589127497217 × 5704689200685129054721	TXT

 	6	264+1	20	274177 × 67280421310721	TXT

 	5	232+1	10	P116 × 6700417	TXT

 	4	216+1	5	P6543	TXT

 	3	28+1	3	P55	TXT

 	2	24+1	2	P7	TXT

 	1	22+1	1	P3	TXT

 	0	21+1	1	P2	TXT

"),
    ]
}<commit_msg>Refactored page to use struct and a loop<commit_after>use seed::prelude::*;
use crate::Msg;
use std::convert::TryInto;

mod fermat_utils {
    use seed::prelude::*;
    use crate::Msg;

    pub struct Fermat<'a> {
		pub n: u64,
		pub f: u64,
		pub digits: u64,
		pub prime_factors: &'a str,
        pub download: &'a str,
	}

    pub fn fermats<'a>() -> Vec<Fermat<'a>> {
        vec![
			Fermat {n: 11, f: 0, digits:  617, prime_factors: "P27567 × 974849 × 167988556341760475137 × 3560841906445833920513 × P564", download: "F0"},
            Fermat {n: 10, f: 0, digits:  309, prime_factors: "45592577 × 6487031809 × 4659775785220018543264560743076778192897 × P252", download: "F0"},
            Fermat {n:  9, f: 0, digits:  155, prime_factors: "2424833 × 7455602825647884208337395736200454918783366342657 × P99", download: "F0"},
            Fermat {n:  8, f: 0, digits:   78, prime_factors: "1238926361552897 × P62", download: "F0"},
            Fermat {n:  7, f: 0, digits:   39, prime_factors: "59649589127497217 × 5704689200685129054721", download: "F0"},
            Fermat {n:  6, f: 0, digits:   20, prime_factors: "274177 × 67280421310721", download: "F0"},
            Fermat {n:  5, f: 0, digits:   10, prime_factors: "P116 × 6700417", download: "F0"},
            Fermat {n:  4, f: 0, digits:    5, prime_factors: "P6543", download: "F0"},
            Fermat {n:  3, f: 0, digits:    3, prime_factors: "P55", download: "F0"},
            Fermat {n:  2, f: 0, digits:    2, prime_factors: "P7", download: "F0"},
            Fermat {n:  1, f: 0, digits:    1, prime_factors: "P3", download: "F0"},
            Fermat {n:  0, f: 0, digits:    1, prime_factors: "P2", download: "F0"},
        ]
    }

    pub fn save_as_file(filename:String, filecontent:String) -> seed::dom_types::Node<Msg> {
        let href:String = vec!["data:text/plain;",&filecontent].into_iter().collect();
        a![attrs!{At::Download => &filename, At::Href => &href}, "TXT"]
    }
}

pub fn render() -> seed::dom_types::Node<Msg> {
    let mut html = vec![];
    let two:usize = 2;

    let mut fermats = fermat_utils::fermats();

    for n in 0..fermats.len() {
        let download_txt:String = vec!["https://static.bigprimes.net/archive/fermat/F",&fermats[n].n.to_string(),".txt"].into_iter().collect();

		let equation:String = vec!["2",&(two.pow((fermats.len()-1-n).try_into().unwrap())).to_string(),"+1"].into_iter().collect();

        html.push(
            tr![
                td![fermats[n].n.to_string()],//rank
				td![El::from_html(&equation)],//fermat number as a formula

                td![fermats[n].digits.to_string()],//digits in length
                td![El::from_html(fermats[n].prime_factors)],//prime factors
                a![attrs!{At::Href => download_txt},"TXT"],//downloads
            ]
        );
    }

    fermats.reverse();

    div![
        h1!["The Fermat Numbers"],
        br![],
        br![],
        br![],
        table![
            attrs!{At::Id => "tbl"},
            tbody![
                tr![
                    td![
                        b!["No."]
                    ],
                    td![
                        b!["Fermat"]
                    ],
                    td![
                        b!["Digits"]
                    ],
                    td![
                        b!["Prime Factors"]
                    ],
                    td![
                        b!["Download"]
                    ]
                ],
                html
            ]
        ]
    ]
}<|endoftext|>