<commit_before>//! The forwarder module shall act as wrapper for the ffi mangled lib.rs
//! calls, which shall be very thin and spare there, but extensive here
//! to prepare clean calls to adbfflutter.
use crate::{
    common::paths::SearchPath,
    ctrl::{ForwardNetMsg, UiUpdateMsg},
    data::{
        collection::Collection,
        ipc::{
            IFCollectionOutputData,
            IPC::{self, DoneSearching},
        },
    },
    net::subs::peer_representation::peer_to_hash_string,
    shared,
};
use async_std::task;
use crossbeam::{
    channel::{unbounded, Receiver, Sender},
    sync::WaitGroup,
};
use libp2p::core::PeerId;
use serde_json;
use std::{
    sync::{Arc, Mutex},
    thread,
};

lazy_static! {
    /// a static immutable runtime for all network activity
    static ref NET_RUNTIME: Mutex<(Receiver<UiUpdateMsg>,Sender<IPC>)> = Mutex::new(create_net_runtime());
    /// a static mutable data collection
    static ref NET_UI : Mutex<UIList> = Mutex::new(UIList { cnt: Vec::new() });
}

/// just return the number of audio files found for now
pub fn ffi_file_count_good(input_path: Vec<String>) -> u32 {
    // prepare data
    let cleaned_paths = SearchPath::new(&input_path);
    let search_path = Arc::new(Mutex::new(cleaned_paths));

    let (tx, _rx) = unbounded::<UiUpdateMsg>();
    let synced_to_ui_messages = Arc::new(Mutex::new(tx.clone()));
    let has_ui = false;

    // set up data and run search
    let collection_protected = Arc::new(Mutex::new(Collection::new()));
    let output_data_return_handle = shared::collection_search(
        collection_protected,
        search_path,
        synced_to_ui_messages,
        has_ui,
    );

    // scope and block trickery for lifetime and mutability
    output_data_return_handle.nr_found_songs
}

/// return the json of peer uis
pub async fn ffi_ui_messages_as_json() -> String {
    // todo: unwrap using NET_UI is save unless you
    //       run this function simultaneously many instances, secure this because
    //       at least 2 functions now use net component, and simultaneous usage
    //       is more probable now!

    // get network runtime
    let (net_receiver, _) = &*NET_RUNTIME.lock().unwrap();

    // break loop if a yet noticeable change happens
    loop {
        if let Ok(reaction) = net_receiver.recv() {
            match reaction {
                UiUpdateMsg::CollectionUpdate(_, _) => {}
                UiUpdateMsg::NetUpdate(net_message) => match net_message {
                    ForwardNetMsg::Add(peer) => {
                        let ui_list = &mut NET_UI.lock().unwrap();
                        {
                            ui_list.add_peer(&peer.id);
                            break;
                        }
                    }
                    ForwardNetMsg::Delete(peer_id) => {
                        let ui_list = &mut NET_UI.lock().unwrap();
                        {
                            ui_list.remove_peer(&peer_id);
                            break;
                        }
                    }
                    ForwardNetMsg::Stats(_) => {}
                },
                UiUpdateMsg::PeerSearchFinished(peer_id, data) => {
                    let ui_list = &mut NET_UI.lock().unwrap();
                    {
                        ui_list.add_search_finished(&peer_id, &data);
                        break;
                    }
                }
                UiUpdateMsg::StopUI => unreachable!(),
            }
        }
    }
    let ui_list = &NET_UI.lock().unwrap();
    {
        let json: &UIList = &*ui_list;
        let peers_ui_json = serde_json::to_string(&json.cnt).unwrap();
        peers_ui_json
    }
}

/// send ipc found out
pub async fn ffi_send_ipc_search_done(nr_searched_files: u32, nr_found_songs: u32) -> bool {
    //
    // get network runtime
    let (_, ipc_sender) = &mut *NET_RUNTIME.lock().unwrap();
    let sending = IFCollectionOutputData {
        nr_searched_files,
        nr_found_songs,
        size_of_data_in_kb: 0,
        nr_internal_duplicates: 0,
    };
    ipc_sender.send(DoneSearching(sending)).is_ok()
}

// ------------------------------------------------------------------------------------------

/// Opens a net thread and return ui message receiver and ipc message sender
/// to be used as static instance for Dart-to-backend communication.
fn create_net_runtime() -> (Receiver<UiUpdateMsg>, Sender<IPC>) {
    // outgoing crossbeam receiver
    let (ui_sender, reactor) = unbounded::<UiUpdateMsg>();
    let (ipc_sender, ipc_receive) = unbounded::<IPC>();
    thread::Builder::new()
        .name("app_net".into())
        .spawn(move || {
            task::block_on(async move {
                // mock input parameters
                let dummy_wait_net_thread = WaitGroup::new();

                match shared::net_search(dummy_wait_net_thread, Some(ui_sender), ipc_receive).await
                {
                    Ok(_) => {}
                    Err(e) => error!("network error: {}", e),
                }
            });
        })
        .unwrap();
    (reactor, ipc_sender)
}

/// Struct viewable for ffi inner part and used as json export
/// !!! change Dart names as well, cause variable names MUST be in sync
#[derive(Serialize, Deserialize, Clone)]
#[allow(non_camel_case_types)]
struct UIListInner {
    // normally peer would be the hash key, but since we use it as json container
    // it's a bit itchy
    peerid: String,
    finished: i32,
    searched: u32,
}
/// Container and helper to fill UIListInner easier
struct UIList {
    pub cnt: Vec<UIListInner>,
}
impl UIList {
    fn add_peer(&mut self, peer_id: &PeerId) {
        let peer_string = peer_to_hash_string(peer_id);
        if self.cnt.iter().find(|e| e.peerid == peer_string).is_none() {
            self.cnt.push(UIListInner {
                peerid: peer_string,
                finished: -1, // -1 on dart side should show that it is not yet completed
                searched: 0,
            });
        }
    }
    fn remove_peer(&mut self, peer_id: &PeerId) {
        //todo: as soon as this is not experimental:
        //      self.cnt.drain_filter(|e| e.peer_id == peer_to_hash(peer_id));
        let peer_string = peer_to_hash_string(peer_id);
        let mut i = 0;
        while i != self.cnt.len() {
            if self.cnt[i].peerid == peer_string {
                self.cnt.remove(i);
                break;
            } else {
                i += 1;
            }
        }
    }
    fn add_search_finished(&mut self, peer_id: &PeerId, data: &IFCollectionOutputData) {
        let peer_string = peer_to_hash_string(peer_id);
        let mut i = 0;
        while i != self.cnt.len() {
            if self.cnt[i].peerid == peer_string {
                self.cnt[i].finished = data.nr_found_songs as i32;
                self.cnt[i].searched = data.nr_searched_files;
                break;
            } else {
                i += 1;
            }
        }
    }
}
<commit_msg>[documentation] added detail for json<commit_after>//! The forwarder module shall act as wrapper for the ffi mangled lib.rs
//! calls, which shall be very thin and spare there, but extensive here
//! to prepare clean calls to adbfflutter.
use crate::{
    common::paths::SearchPath,
    ctrl::{ForwardNetMsg, UiUpdateMsg},
    data::{
        collection::Collection,
        ipc::{
            IFCollectionOutputData,
            IPC::{self, DoneSearching},
        },
    },
    net::subs::peer_representation::peer_to_hash_string,
    shared,
};
use async_std::task;
use crossbeam::{
    channel::{unbounded, Receiver, Sender},
    sync::WaitGroup,
};
use libp2p::core::PeerId;
use serde_json;
use std::{
    sync::{Arc, Mutex},
    thread,
};

lazy_static! {
    /// a static immutable runtime for all network activity
    static ref NET_RUNTIME: Mutex<(Receiver<UiUpdateMsg>,Sender<IPC>)> = Mutex::new(create_net_runtime());
    /// a static mutable data collection
    static ref NET_UI : Mutex<UIList> = Mutex::new(UIList { cnt: Vec::new() });
}

/// just return the number of audio files found for now
pub fn ffi_file_count_good(input_path: Vec<String>) -> u32 {
    // prepare data
    let cleaned_paths = SearchPath::new(&input_path);
    let search_path = Arc::new(Mutex::new(cleaned_paths));

    let (tx, _rx) = unbounded::<UiUpdateMsg>();
    let synced_to_ui_messages = Arc::new(Mutex::new(tx.clone()));
    let has_ui = false;

    // set up data and run search
    let collection_protected = Arc::new(Mutex::new(Collection::new()));
    let output_data_return_handle = shared::collection_search(
        collection_protected,
        search_path,
        synced_to_ui_messages,
        has_ui,
    );

    // scope and block trickery for lifetime and mutability
    output_data_return_handle.nr_found_songs
}

/// Return the json of peer uis which holds a vector of UIListInner
pub async fn ffi_ui_messages_as_json() -> String {
    // todo: unwrap using NET_UI is save unless you
    //       run this function simultaneously many instances, secure this because
    //       at least 2 functions now use net component, and simultaneous usage
    //       is more probable now!

    // get network runtime
    let (net_receiver, _) = &*NET_RUNTIME.lock().unwrap();

    // break loop if a yet noticeable change happens
    loop {
        if let Ok(reaction) = net_receiver.recv() {
            match reaction {
                UiUpdateMsg::CollectionUpdate(_, _) => {}
                UiUpdateMsg::NetUpdate(net_message) => match net_message {
                    ForwardNetMsg::Add(peer) => {
                        let ui_list = &mut NET_UI.lock().unwrap();
                        {
                            ui_list.add_peer(&peer.id);
                            break;
                        }
                    }
                    ForwardNetMsg::Delete(peer_id) => {
                        let ui_list = &mut NET_UI.lock().unwrap();
                        {
                            ui_list.remove_peer(&peer_id);
                            break;
                        }
                    }
                    ForwardNetMsg::Stats(_) => {}
                },
                UiUpdateMsg::PeerSearchFinished(peer_id, data) => {
                    let ui_list = &mut NET_UI.lock().unwrap();
                    {
                        ui_list.add_search_finished(&peer_id, &data);
                        break;
                    }
                }
                UiUpdateMsg::StopUI => unreachable!(),
            }
        }
    }
    let ui_list = &NET_UI.lock().unwrap();
    {
        let json: &UIList = &*ui_list;
        let peers_ui_json = serde_json::to_string(&json.cnt).unwrap();
        peers_ui_json
    }
}

/// send ipc found out
pub async fn ffi_send_ipc_search_done(nr_searched_files: u32, nr_found_songs: u32) -> bool {
    //
    // get network runtime
    let (_, ipc_sender) = &mut *NET_RUNTIME.lock().unwrap();
    let sending = IFCollectionOutputData {
        nr_searched_files,
        nr_found_songs,
        size_of_data_in_kb: 0,
        nr_internal_duplicates: 0,
    };
    ipc_sender.send(DoneSearching(sending)).is_ok()
}

// ------------------------------------------------------------------------------------------

/// Opens a net thread and return ui message receiver and ipc message sender
/// to be used as static instance for Dart-to-backend communication.
fn create_net_runtime() -> (Receiver<UiUpdateMsg>, Sender<IPC>) {
    // outgoing crossbeam receiver
    let (ui_sender, reactor) = unbounded::<UiUpdateMsg>();
    let (ipc_sender, ipc_receive) = unbounded::<IPC>();
    thread::Builder::new()
        .name("app_net".into())
        .spawn(move || {
            task::block_on(async move {
                // mock input parameters
                let dummy_wait_net_thread = WaitGroup::new();

                match shared::net_search(dummy_wait_net_thread, Some(ui_sender), ipc_receive).await
                {
                    Ok(_) => {}
                    Err(e) => error!("network error: {}", e),
                }
            });
        })
        .unwrap();
    (reactor, ipc_sender)
}

/// Struct viewable for ffi inner part and used as json export
/// !!! change Dart names as well, cause variable names MUST be in sync
#[derive(Serialize, Deserialize, Clone)]
#[allow(non_camel_case_types)]
struct UIListInner {
    // normally peer would be the hash key, but since we use it as json container
    // it's a bit itchy
    peerid: String,
    finished: i32,
    searched: u32,
}
/// Container and helper to fill UIListInner easier
struct UIList {
    pub cnt: Vec<UIListInner>,
}
impl UIList {
    fn add_peer(&mut self, peer_id: &PeerId) {
        let peer_string = peer_to_hash_string(peer_id);
        if self.cnt.iter().find(|e| e.peerid == peer_string).is_none() {
            self.cnt.push(UIListInner {
                peerid: peer_string,
                finished: -1, // -1 on dart side should show that it is not yet completed
                searched: 0,
            });
        }
    }
    fn remove_peer(&mut self, peer_id: &PeerId) {
        //todo: as soon as this is not experimental:
        //      self.cnt.drain_filter(|e| e.peer_id == peer_to_hash(peer_id));
        let peer_string = peer_to_hash_string(peer_id);
        let mut i = 0;
        while i != self.cnt.len() {
            if self.cnt[i].peerid == peer_string {
                self.cnt.remove(i);
                break;
            } else {
                i += 1;
            }
        }
    }
    fn add_search_finished(&mut self, peer_id: &PeerId, data: &IFCollectionOutputData) {
        let peer_string = peer_to_hash_string(peer_id);
        let mut i = 0;
        while i != self.cnt.len() {
            if self.cnt[i].peerid == peer_string {
                self.cnt[i].finished = data.nr_found_songs as i32;
                self.cnt[i].searched = data.nr_searched_files;
                break;
            } else {
                i += 1;
            }
        }
    }
}
<|endoftext|>


<commit_before>
use iron::{Iron, IronResult, Listening, status};
use iron::error::HttpResult;
use iron::response::Response;
#[cfg(feature = "gzip")]
use iron::response::WriteBody;
use iron::request::Request;
use iron::middleware::Handler;
use iron::mime::Mime;

use sysinfo::{System, SystemExt};

#[cfg(feature = "gzip")]
use flate2::Compression;
#[cfg(feature = "gzip")]
use flate2::write::GzEncoder;

use std::sync::{Arc, Mutex, RwLock};
use std::thread;
use std::time::{Duration, SystemTime};
#[cfg(feature = "gzip")]
use std::io::{self, Write};

use SysinfoExt;
use serde_json;

const INDEX_HTML: &'static [u8] = include_bytes!("index.html");
const FAVICON: &'static [u8] = include_bytes!("../resources/favicon.ico");
const REFRESH_DELAY: u64 = 60 * 10; // 10 minutes

/// Simple wrapper to get gzip compressed output on string types.
#[cfg(feature = "gzip")]
struct GzipContent(Box<WriteBody>);

#[cfg(feature = "gzip")]
impl WriteBody for GzipContent {
    fn write_body(&mut self, w: &mut Write) -> io::Result<()> {
        let mut w = GzEncoder::new(w, Compression::default());
        self.0.write_body(&mut w)?;
        w.finish().map(|_| ())
    }
}

struct SysinfoIronHandler(Arc<DataHandler>);

struct DataHandler {
    system: RwLock<System>,
    last_connection: Mutex<SystemTime>,
    json_output: RwLock<String>,
}

impl DataHandler {
    fn can_update_system_info(&self) -> bool {
        SystemTime::now().duration_since(*self.last_connection.lock().unwrap())
                         .unwrap()
                         .as_secs() < REFRESH_DELAY
    }

    fn update_last_connection(&self) {
        *self.last_connection.lock().unwrap() = SystemTime::now();
    }
}

#[cfg(feature = "gzip")]
macro_rules! return_gzip_or_not {
    ($req:expr, $content:expr, $typ:expr) => {{
        let mut use_gzip = false;

        if let Some(raw_accept_encoding) = $req.headers.get_raw("accept-encoding") {
            for accept_encoding in raw_accept_encoding {
                match ::std::str::from_utf8(accept_encoding).map(|s| s.to_lowercase()) {
                    Ok(ref s) if s.contains("gzip") => {
                        use_gzip = true;
                        break;
                    }
                    _ => continue,
                }
            }
        }
        if !use_gzip {
            Ok(Response::with((status::Ok, $typ.parse::<Mime>().unwrap(), $content)))
        } else {
            let mut res = Response::new();
            res.status = Some(status::Ok);
            res.body = Some(Box::new(GzipContent(Box::new($content))));
            res.headers.append_raw("content-type", $typ.as_bytes().to_vec());
            res.headers.append_raw("content-encoding", vec![b'g', b'z', b'i', b'p']);
            Ok(res)
        }
    }}
}

#[cfg(not(feature = "gzip"))]
macro_rules! return_gzip_or_not {
    ($req:expr, $content:expr, $typ:expr) => {{
        Ok(Response::with((status::Ok, $typ.parse::<Mime>().unwrap(), $content)))
    }}
}

impl Handler for SysinfoIronHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        match match req.url.path().last() {
            Some(path) => {
                if *path == "" {
                    1
                } else if *path == "favicon.ico" {
                    2
                } else {
                    3
                }
            }
            None => 0,
        } {
            1 => return_gzip_or_not!(req, INDEX_HTML, "text/html"),
            2 => return_gzip_or_not!(req, FAVICON, "image/x-icon"),
            3 => {
                self.0.update_last_connection();
                return_gzip_or_not!(req,
                                    self.0.json_output.read().unwrap().clone(),
                                    "application/json")
            }
            _ => Ok(Response::with((status::NotFound, "Not found"))),
        }
    }
}

pub fn start_web_server(sock_addr: Option<String>) -> HttpResult<Listening> {
    let data_handler = Arc::new(DataHandler {
        system: RwLock::new(System::new()),
        last_connection: Mutex::new(SystemTime::now()),
        json_output: RwLock::new(String::from("[]")),
    });
    let data_handler_clone = data_handler.clone();
    thread::spawn(move || {
        let mut sleeping = false;
        loop {
            if data_handler_clone.can_update_system_info() {
                {
                    let mut system = data_handler_clone.system.write().unwrap();
                    system.refresh_all();
                    // refresh it twice to provide accurate information after wake up
                    if sleeping {
                        system.refresh_all();
                        sleeping = false;
                    }
                    let sysinfo = SysinfoExt::new(&system);
                    let mut json_output = data_handler_clone.json_output.write().unwrap();
                    json_output.clear();
                    use std::fmt::Write;
                    json_output.write_str(&serde_json::to_string(&sysinfo)
                                          .unwrap_or(String::from("[]"))).unwrap();
                }
                thread::sleep(Duration::new(5, 0));
            } else {
                // If we don't need to refresh the system information, we can sleep a lot less.
                thread::sleep(Duration::from_millis(500));
                sleeping = true;
            }
        }
    });
    let mut iron = Iron::new(SysinfoIronHandler(data_handler));
    iron.threads = 4;
    iron.http(sock_addr.unwrap_or("localhost:3000".to_owned()))
}
<commit_msg>Use iron types for headers<commit_after>
use iron::{Iron, IronResult, Listening, status};
use iron::error::HttpResult;
use iron::response::Response;
#[cfg(feature = "gzip")]
use iron::response::WriteBody;
use iron::request::Request;
use iron::middleware::Handler;
use iron::mime::Mime;

use sysinfo::{System, SystemExt};

#[cfg(feature = "gzip")]
use flate2::Compression;
#[cfg(feature = "gzip")]
use flate2::write::GzEncoder;

use std::sync::{Arc, Mutex, RwLock};
use std::thread;
use std::time::{Duration, SystemTime};
#[cfg(feature = "gzip")]
use std::io::{self, Write};

use SysinfoExt;
use serde_json;

const INDEX_HTML: &'static [u8] = include_bytes!("index.html");
const FAVICON: &'static [u8] = include_bytes!("../resources/favicon.ico");
const REFRESH_DELAY: u64 = 60 * 10; // 10 minutes

/// Simple wrapper to get gzip compressed output on string types.
#[cfg(feature = "gzip")]
struct GzipContent(Box<WriteBody>);

#[cfg(feature = "gzip")]
impl WriteBody for GzipContent {
    fn write_body(&mut self, w: &mut Write) -> io::Result<()> {
        let mut w = GzEncoder::new(w, Compression::default());
        self.0.write_body(&mut w)?;
        w.finish().map(|_| ())
    }
}

struct SysinfoIronHandler(Arc<DataHandler>);

struct DataHandler {
    system: RwLock<System>,
    last_connection: Mutex<SystemTime>,
    json_output: RwLock<String>,
}

impl DataHandler {
    fn can_update_system_info(&self) -> bool {
        SystemTime::now().duration_since(*self.last_connection.lock().unwrap())
                         .unwrap()
                         .as_secs() < REFRESH_DELAY
    }

    fn update_last_connection(&self) {
        *self.last_connection.lock().unwrap() = SystemTime::now();
    }
}

#[cfg(feature = "gzip")]
macro_rules! return_gzip_or_not {
    ($req:expr, $content:expr, $typ:expr) => {{
        let mut use_gzip = false;

        if let Some(raw_accept_encoding) = $req.headers.get_raw("accept-encoding") {
            for accept_encoding in raw_accept_encoding {
                match ::std::str::from_utf8(accept_encoding).map(|s| s.to_lowercase()) {
                    Ok(ref s) if s.contains("gzip") => {
                        use_gzip = true;
                        break;
                    }
                    _ => continue,
                }
            }
        }
        if !use_gzip {
            Ok(Response::with((status::Ok, $typ.parse::<Mime>().unwrap(), $content)))
        } else {
            use iron::headers::{ContentType, ContentEncoding, Encoding};
            let mut res = Response::new();
            res.status = Some(status::Ok);
            res.body = Some(Box::new(GzipContent(Box::new($content))));
            res.headers.set(ContentType($typ.parse::<Mime>().unwrap()));
            res.headers.set(ContentEncoding(vec![Encoding::Gzip]));
            Ok(res)
        }
    }}
}

#[cfg(not(feature = "gzip"))]
macro_rules! return_gzip_or_not {
    ($req:expr, $content:expr, $typ:expr) => {{
        Ok(Response::with((status::Ok, $typ.parse::<Mime>().unwrap(), $content)))
    }}
}

impl Handler for SysinfoIronHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        match match req.url.path().last() {
            Some(path) => {
                if *path == "" {
                    1
                } else if *path == "favicon.ico" {
                    2
                } else {
                    3
                }
            }
            None => 0,
        } {
            1 => return_gzip_or_not!(req, INDEX_HTML, "text/html"),
            2 => return_gzip_or_not!(req, FAVICON, "image/x-icon"),
            3 => {
                self.0.update_last_connection();
                return_gzip_or_not!(req,
                                    self.0.json_output.read().unwrap().clone(),
                                    "application/json")
            }
            _ => Ok(Response::with((status::NotFound, "Not found"))),
        }
    }
}

pub fn start_web_server(sock_addr: Option<String>) -> HttpResult<Listening> {
    let data_handler = Arc::new(DataHandler {
        system: RwLock::new(System::new()),
        last_connection: Mutex::new(SystemTime::now()),
        json_output: RwLock::new(String::from("[]")),
    });
    let data_handler_clone = data_handler.clone();
    thread::spawn(move || {
        let mut sleeping = false;
        loop {
            if data_handler_clone.can_update_system_info() {
                {
                    let mut system = data_handler_clone.system.write().unwrap();
                    system.refresh_all();
                    // refresh it twice to provide accurate information after wake up
                    if sleeping {
                        system.refresh_all();
                        sleeping = false;
                    }
                    let sysinfo = SysinfoExt::new(&system);
                    let mut json_output = data_handler_clone.json_output.write().unwrap();
                    json_output.clear();
                    use std::fmt::Write;
                    json_output.write_str(&serde_json::to_string(&sysinfo)
                                          .unwrap_or(String::from("[]"))).unwrap();
                }
                thread::sleep(Duration::new(5, 0));
            } else {
                // If we don't need to refresh the system information, we can sleep a lot less.
                thread::sleep(Duration::from_millis(500));
                sleeping = true;
            }
        }
    });
    let mut iron = Iron::new(SysinfoIronHandler(data_handler));
    iron.threads = 4;
    iron.http(sock_addr.unwrap_or("localhost:3000".to_owned()))
}
<|endoftext|>


<commit_before>// Lumol, an extensible molecular simulation engine
// Copyright (C) 2015-2016 G. Fraux — BSD license

//! Caching energy components for speeding up the energy computation in
//! Monte-Carlo simulations.
//!
//! In most of Monte-Carlo moves, only a very small subset of the system changes.
//! We can use that property to remove the need of recomputing most of the
//! energy components, by storing them and providing update callbacks.
use std::mem;

use super::System;
use types::{Vector3D, Array2};

/// Callback for updating a cache. It also take an `&mut System` argument for
/// updating the cache inside the global potentials.
type UpdateCallback = Box<Fn(&mut EnergyCache, &mut System)>;

/// This is a cache for energy computation.
///
/// Cache integrity is left up to the user of this structure: any function with
/// `update_` prefix must be called as needed to ensure cache consistency.
pub struct EnergyCache {
    /// 2-D array containing the pairs interactions between particles `i` and
    /// `j` at index `i, j`
    pairs_cache: Array2<f64>,
    /// Energy of all the pairs in the system
    pairs: f64,
    /// Energy of all the bonds in the system
    bonds: f64,
    /// Energy of all the angles in the system
    angles: f64,
    /// Energy of all the dihedrals angles in the system
    dihedrals: f64,
    /// Energy of coulombic interactions
    coulomb: f64,
    /// Energy of global interactions
    global: f64,
    /// Callback to be called to update the cache if the system is modified
    updater: Option<UpdateCallback>
}

impl Default for EnergyCache {
    fn default() -> EnergyCache {
        EnergyCache::new()
    }
}

impl EnergyCache {
    /// Create a new empty energy cache.
    pub fn new() -> EnergyCache {
        EnergyCache {
            pairs_cache: Array2::zeros((0, 0)),
            pairs: 0.0,
            bonds: 0.0,
            angles: 0.0,
            dihedrals: 0.0,
            coulomb: 0.0,
            global: 0.0,
            updater: None,
        }
    }

    /// Clear all values in the cache by setting them to 0
    fn clear(&mut self) {
        self.pairs_cache.assign(0.0);
        self.pairs = 0.0;
        self.bonds = 0.0;
        self.angles = 0.0;
        self.dihedrals = 0.0;
        self.coulomb = 0.0;
        self.global = 0.0;
    }

    /// Initialize the cache to be used with `system`. After a call to this
    /// function, the cache is only usable with the same system. To change
    /// the associated system, one must call this function again.
    pub fn init(&mut self, system: &System) {
        self.clear();
        self.pairs_cache.resize_if_different((system.size(), system.size()));

        let evaluator = system.energy_evaluator();

        for i in 0..system.size() {
            for j in (i + 1)..system.size() {
                let r = system.nearest_image(i, j).norm();
                let energy = evaluator.pair(r, i, j);
                self.pairs_cache[(i, j)] = energy;
                self.pairs_cache[(j, i)] = energy;
                self.pairs += energy;
            }
        }

        self.bonds = evaluator.bonds();
        self.angles = evaluator.angles();
        self.dihedrals = evaluator.dihedrals();
        self.coulomb = evaluator.coulomb();
        self.global = evaluator.global();
    }

    /// Get the cached energy
    pub fn energy(&self) -> f64 {
        let mut energy = 0.0;
        energy += self.pairs;

        energy += self.bonds;
        energy += self.angles;
        energy += self.dihedrals;

        energy += self.coulomb;
        energy += self.global;
        return energy;
    }

    /// Update the cache after a call to a `EnergyCache::*_cost` function or
    /// `EnergyCache::unused`.
    pub fn update(&mut self, system: &mut System) {
        let updater = mem::replace(&mut self.updater, None);
        if let Some(updater) = updater {
            updater(self, system);
        } else {
            fatal_error!("Error in `EnergyCache::update`: \
                         This function MUST be called after a call to a `*_cost` function");
        }
    }

    /// This function should be called whenever the cache is not used, but one
    /// still want it to be updated. Future call to `EnergyCache::update` will
    /// recompute the full cache.
    pub fn unused(&mut self) {
        self.updater = Some(Box::new(|cache, system| {
            cache.init(system);
        }))
    }
}

impl EnergyCache {
    /// Get the cost of moving the set of particles with indexes in `idxes` to
    /// `newpos`. This function DO NOT update the cache, the
    /// `update_particles_moved` function MUST be called if the particles are
    /// effectively moved.
    pub fn move_particles_cost(&mut self, system: &System, idxes: Vec<usize>, newpos: &[Vector3D]) -> f64 {
        let evaluator = system.energy_evaluator();

        // First, go for pair interactions
        let mut new_pairs = Array2::<f64>::zeros((system.size(), system.size()));
        let mut pairs_delta = 0.0;
        // Interactions with the sub-system not being moved
        for (i, &part_i) in idxes.iter().enumerate() {
            for part_j in 0..system.size() {
                // Exclude interactions inside the sub-system.
                if idxes.contains(&part_j) {continue}

                let r = system.cell().distance(&system[part_j].position, &newpos[i]);
                let energy = evaluator.pair(r, part_i, part_j);

                pairs_delta += energy;
                new_pairs[(part_i, part_j)] += energy;
                new_pairs[(part_j, part_i)] += energy;

                pairs_delta -= self.pairs_cache[(part_i, part_j)];
            }
        }

        // Recompute everything here. One day we could store some terms and do
        // the same thing than for pair interactions.
        let mut bonds = 0.0;
        let mut angles = 0.0;
        let mut dihedrals = 0.0;
        for molecule in system.molecules() {
            for bond in molecule.bonds() {
                let (i, j) = (bond.i(), bond.j());
                let ri = new_position(system, i, &idxes, newpos);
                let rj = new_position(system, j, &idxes, newpos);
                let r = system.cell().distance(ri, rj);
                bonds += evaluator.bond(r, i, j);
            }

            for angle in molecule.angles() {
                let (i, j, k) = (angle.i(), angle.j(), angle.k());
                let ri = new_position(system, i, &idxes, newpos);
                let rj = new_position(system, j, &idxes, newpos);
                let rk = new_position(system, k, &idxes, newpos);
                let theta = system.cell().angle(ri, rj, rk);
                angles += evaluator.angle(theta, i, j, k);
            }

            for dihedral in molecule.dihedrals() {
                let (i, j, k, m) = (dihedral.i(), dihedral.j(), dihedral.k(), dihedral.m());
                let ri = new_position(system, i, &idxes, newpos);
                let rj = new_position(system, j, &idxes, newpos);
                let rk = new_position(system, k, &idxes, newpos);
                let rm = new_position(system, m, &idxes, newpos);
                let phi = system.cell().dihedral(ri, rj, rk, rm);
                dihedrals += evaluator.dihedral(phi, i, j, k, m);
            }
        }

        let coulomb_delta = if let Some(coulomb) = system.interactions().coulomb() {
            coulomb.borrow_mut().move_particles_cost(system, &idxes, newpos)
        } else {
            0.0
        };

        let mut global_delta = 0.0;
        for potential in system.interactions().globals() {
            global_delta += potential.borrow_mut().move_particles_cost(system, &idxes, newpos);
        }

        let cost = pairs_delta + (bonds - self.bonds)
                               + (angles - self.angles)
                               + (dihedrals - self.dihedrals)
                               + coulomb_delta + global_delta;

        self.updater = Some(Box::new(move |cache, system| {
            cache.bonds = bonds;
            cache.angles = angles;
            cache.dihedrals = dihedrals;

            cache.pairs += pairs_delta;
            cache.coulomb += coulomb_delta;
            cache.global += global_delta;

            let (n, m) = new_pairs.shape();
            debug_assert!(n == m);
            debug_assert!((n, m) == cache.pairs_cache.shape());
            // only loop over the indices that actually changed
            for i in idxes.iter() {
                for j in 0..n {
                    if idxes.contains(&j) {continue}
                    cache.pairs_cache[(*i, j)] = new_pairs[(*i, j)];
                    cache.pairs_cache[(j, *i)] = new_pairs[(*i, j)];
                }
            }
            // Update the cache for the global potentials
            if let Some(coulomb) = system.interactions().coulomb() {
                coulomb.borrow_mut().update();
            }
            for potential in system.interactions().globals() {
                potential.borrow_mut().update();
            }
        }));
        return cost;
    }
}

/// Return either the new position of a particle (from `newpos`) if its index
/// is in `idxes`, or its old position in the system.
fn new_position<'a>(system: &'a System, i: usize, idxes: &[usize], newpos: &'a[Vector3D]) -> &'a Vector3D {
    for (idx, &particle) in idxes.iter().enumerate() {
        if particle == i {
            return &newpos[idx];
        }
    }
    return &system[i].position;
}

#[cfg(test)]
mod tests {
    use super::*;
    use sys::{System, UnitCell};
    use energy::*;
    use utils::{system_from_xyz, unit_from};
    use types::Vector3D;

    fn testing_system() -> System {
        let mut system = system_from_xyz("4
        bonds
        O     0.000000     0.000000     0.000000
        O     0.000000     0.000000     1.480000
        H     0.895669     0.000000    -0.316667
        H    -0.895669     0.000000     1.796667");
        system.set_cell(UnitCell::cubic(10.0));
        assert!(system.molecules().len() == 1);

        system.interactions_mut().add_pair("H", "H", PairInteraction::new(
            Box::new(LennardJones{sigma: 3.0, epsilon: unit_from(0.5, "kJ/mol")}), 3.0
        ));

        system.interactions_mut().add_pair("O", "O", PairInteraction::new(
            Box::new(NullPotential), 3.0
        ));

        system.interactions_mut().add_pair("O", "H", PairInteraction::new(
            Box::new(LennardJones{sigma: 1.0, epsilon: unit_from(0.3, "kJ/mol")}), 3.0
        ));

        system.interactions_mut().add_bond("O", "H",
            Box::new(Harmonic{x0: 3.4, k: unit_from(522.0, "kJ/mol/A^2")})
        );

        system.interactions_mut().add_angle("O", "O", "H",
            Box::new(Harmonic{x0: f64::to_radians(120.0), k: unit_from(150.0, "kJ/mol/deg^2")})
        );

        system.interactions_mut().add_dihedral("H", "O", "O", "H",
            Box::new(Harmonic{x0: f64::to_radians(180.0), k: unit_from(800.0, "kJ/mol/deg^2")})
        );

        system.interactions_mut().set_coulomb(Box::new(Wolf::new(8.0)));

        for atom in &mut system {
            if atom.name() == "O" {
                atom.charge = -0.5;
            } else if atom.name() == "H" {
                atom.charge = 0.5;
            }
        }

        return system;
    }

    #[test]
    fn cache_energy() {
        let system = testing_system();
        let mut cache = EnergyCache::new();
        cache.init(&system);

        assert_approx_eq!(cache.energy(), system.potential_energy());
    }

    #[test]
    fn move_atoms() {
        let mut system = testing_system();
        let mut cache = EnergyCache::new();
        let old_e = system.potential_energy();
        cache.init(&system);

        let idxes = vec![0, 3];
        let newpos = &[Vector3D::new(0.0, 0.0, 0.5), Vector3D::new(-0.7, 0.2, 1.5)];

        let cost = cache.move_particles_cost(&system, idxes, newpos);

        system[0].position = newpos[0];
        system[3].position = newpos[1];
        let new_e = system.potential_energy();
        assert_approx_eq!(cost, new_e - old_e, 1e-14);

        cache.update(&mut system);
        assert_approx_eq!(cache.energy(), new_e, 1e-14);

        // Check that the cache is really updated
        let old_e = new_e;
        let idxes = vec![2, 3];
        let newpos = &[Vector3D::new(0.9, 0.2, -0.4), Vector3D::new(-0.9, 0.0, 1.8)];
        let cost = cache.move_particles_cost(&system, idxes, newpos);
        system[2].position = newpos[0];
        system[3].position = newpos[1];
        let new_e = system.potential_energy();
        assert_approx_eq!(cost, new_e - old_e, 1e-14);
    }
}
<commit_msg>fixed test case<commit_after>// Lumol, an extensible molecular simulation engine
// Copyright (C) 2015-2016 G. Fraux — BSD license

//! Caching energy components for speeding up the energy computation in
//! Monte-Carlo simulations.
//!
//! In most of Monte-Carlo moves, only a very small subset of the system changes.
//! We can use that property to remove the need of recomputing most of the
//! energy components, by storing them and providing update callbacks.
use std::mem;

use super::System;
use types::{Vector3D, Array2};

/// Callback for updating a cache. It also take an `&mut System` argument for
/// updating the cache inside the global potentials.
type UpdateCallback = Box<Fn(&mut EnergyCache, &mut System)>;

/// This is a cache for energy computation.
///
/// Cache integrity is left up to the user of this structure: any function with
/// `update_` prefix must be called as needed to ensure cache consistency.
pub struct EnergyCache {
    /// 2-D array containing the pairs interactions between particles `i` and
    /// `j` at index `i, j`
    pairs_cache: Array2<f64>,
    /// Energy of all the pairs in the system
    pairs: f64,
    /// Energy of all the bonds in the system
    bonds: f64,
    /// Energy of all the angles in the system
    angles: f64,
    /// Energy of all the dihedrals angles in the system
    dihedrals: f64,
    /// Energy of coulombic interactions
    coulomb: f64,
    /// Energy of global interactions
    global: f64,
    /// Callback to be called to update the cache if the system is modified
    updater: Option<UpdateCallback>
}

impl Default for EnergyCache {
    fn default() -> EnergyCache {
        EnergyCache::new()
    }
}

impl EnergyCache {
    /// Create a new empty energy cache.
    pub fn new() -> EnergyCache {
        EnergyCache {
            pairs_cache: Array2::zeros((0, 0)),
            pairs: 0.0,
            bonds: 0.0,
            angles: 0.0,
            dihedrals: 0.0,
            coulomb: 0.0,
            global: 0.0,
            updater: None,
        }
    }

    /// Clear all values in the cache by setting them to 0
    fn clear(&mut self) {
        self.pairs_cache.assign(0.0);
        self.pairs = 0.0;
        self.bonds = 0.0;
        self.angles = 0.0;
        self.dihedrals = 0.0;
        self.coulomb = 0.0;
        self.global = 0.0;
    }

    /// Initialize the cache to be used with `system`. After a call to this
    /// function, the cache is only usable with the same system. To change
    /// the associated system, one must call this function again.
    pub fn init(&mut self, system: &System) {
        self.clear();
        self.pairs_cache.resize_if_different((system.size(), system.size()));

        let evaluator = system.energy_evaluator();

        for i in 0..system.size() {
            for j in (i + 1)..system.size() {
                let r = system.nearest_image(i, j).norm();
                let energy = evaluator.pair(r, i, j);
                self.pairs_cache[(i, j)] = energy;
                self.pairs_cache[(j, i)] = energy;
                self.pairs += energy;
            }
        }

        self.bonds = evaluator.bonds();
        self.angles = evaluator.angles();
        self.dihedrals = evaluator.dihedrals();
        self.coulomb = evaluator.coulomb();
        self.global = evaluator.global();
    }

    /// Get the cached energy
    pub fn energy(&self) -> f64 {
        let mut energy = 0.0;
        energy += self.pairs;

        energy += self.bonds;
        energy += self.angles;
        energy += self.dihedrals;

        energy += self.coulomb;
        energy += self.global;
        return energy;
    }

    /// Update the cache after a call to a `EnergyCache::*_cost` function or
    /// `EnergyCache::unused`.
    pub fn update(&mut self, system: &mut System) {
        let updater = mem::replace(&mut self.updater, None);
        if let Some(updater) = updater {
            updater(self, system);
        } else {
            fatal_error!("Error in `EnergyCache::update`: \
                         This function MUST be called after a call to a `*_cost` function");
        }
    }

    /// This function should be called whenever the cache is not used, but one
    /// still want it to be updated. Future call to `EnergyCache::update` will
    /// recompute the full cache.
    pub fn unused(&mut self) {
        self.updater = Some(Box::new(|cache, system| {
            cache.init(system);
        }))
    }
}

impl EnergyCache {
    /// Get the cost of moving the set of particles with indexes in `idxes` to
    /// `newpos`. This function DO NOT update the cache, the
    /// `update_particles_moved` function MUST be called if the particles are
    /// effectively moved.
    pub fn move_particles_cost(&mut self, system: &System, idxes: Vec<usize>, newpos: &[Vector3D]) -> f64 {
        let evaluator = system.energy_evaluator();

        // First, go for pair interactions
        let mut new_pairs = Array2::<f64>::zeros((system.size(), system.size()));
        let mut pairs_delta = 0.0;
        // Interactions with the sub-system not being moved
        for (i, &part_i) in idxes.iter().enumerate() {
            for part_j in 0..system.size() {
                // Exclude interactions inside the sub-system.
                if idxes.contains(&part_j) {continue}

                let r = system.cell().distance(&system[part_j].position, &newpos[i]);
                let energy = evaluator.pair(r, part_i, part_j);

                pairs_delta += energy;
                new_pairs[(part_i, part_j)] += energy;
                new_pairs[(part_j, part_i)] += energy;

                pairs_delta -= self.pairs_cache[(part_i, part_j)];
            }
        }

        // Interactions withing the sub-system being moved
        for (i, &part_i) in idxes.iter().enumerate() {
            for (j, &part_j) in idxes.iter().enumerate().skip(i + 1) {
                let r = system.cell().distance(&newpos[i], &newpos[j]);
                let energy = evaluator.pair(r, part_i, part_j);

                pairs_delta += energy;
                new_pairs[(part_i, part_j)] += energy;
                new_pairs[(part_j, part_i)] += energy;

                pairs_delta -= self.pairs_cache[(part_i, part_j)];
            }
        }

        // Recompute everything here. One day we could store some terms and do
        // the same thing than for pair interactions.
        let mut bonds = 0.0;
        let mut angles = 0.0;
        let mut dihedrals = 0.0;
        for molecule in system.molecules() {
            for bond in molecule.bonds() {
                let (i, j) = (bond.i(), bond.j());
                let ri = new_position(system, i, &idxes, newpos);
                let rj = new_position(system, j, &idxes, newpos);
                let r = system.cell().distance(ri, rj);
                bonds += evaluator.bond(r, i, j);
            }

            for angle in molecule.angles() {
                let (i, j, k) = (angle.i(), angle.j(), angle.k());
                let ri = new_position(system, i, &idxes, newpos);
                let rj = new_position(system, j, &idxes, newpos);
                let rk = new_position(system, k, &idxes, newpos);
                let theta = system.cell().angle(ri, rj, rk);
                angles += evaluator.angle(theta, i, j, k);
            }

            for dihedral in molecule.dihedrals() {
                let (i, j, k, m) = (dihedral.i(), dihedral.j(), dihedral.k(), dihedral.m());
                let ri = new_position(system, i, &idxes, newpos);
                let rj = new_position(system, j, &idxes, newpos);
                let rk = new_position(system, k, &idxes, newpos);
                let rm = new_position(system, m, &idxes, newpos);
                let phi = system.cell().dihedral(ri, rj, rk, rm);
                dihedrals += evaluator.dihedral(phi, i, j, k, m);
            }
        }

        let coulomb_delta = if let Some(coulomb) = system.interactions().coulomb() {
            coulomb.borrow_mut().move_particles_cost(system, &idxes, newpos)
        } else {
            0.0
        };

        let mut global_delta = 0.0;
        for potential in system.interactions().globals() {
            global_delta += potential.borrow_mut().move_particles_cost(system, &idxes, newpos);
        }

        let cost = pairs_delta + (bonds - self.bonds)
                               + (angles - self.angles)
                               + (dihedrals - self.dihedrals)
                               + coulomb_delta + global_delta;

        self.updater = Some(Box::new(move |cache, system| {
            cache.bonds = bonds;
            cache.angles = angles;
            cache.dihedrals = dihedrals;

            cache.pairs += pairs_delta;
            cache.coulomb += coulomb_delta;
            cache.global += global_delta;

            let (n, m) = new_pairs.shape();
            debug_assert!(n == m);
            debug_assert!((n, m) == cache.pairs_cache.shape());
            // only loop over the indices that actually changed
            for &i in idxes.iter() {
                for j in 0..n {
                    if idxes.contains(&j) {continue}
                    cache.pairs_cache[(i, j)] = new_pairs[(i, j)];
                    cache.pairs_cache[(j, i)] = new_pairs[(i, j)];
                }
                for &k in idxes.iter().skip(i + 1) {
                    cache.pairs_cache[(i, k)] = new_pairs[(i, k)];
                    cache.pairs_cache[(k, i)] = new_pairs[(i, k)];
                }
            }

            // Update the cache for the global potentials
            if let Some(coulomb) = system.interactions().coulomb() {
                coulomb.borrow_mut().update();
            }
            for potential in system.interactions().globals() {
                potential.borrow_mut().update();
            }
        }));
        return cost;
    }
}

/// Return either the new position of a particle (from `newpos`) if its index
/// is in `idxes`, or its old position in the system.
fn new_position<'a>(system: &'a System, i: usize, idxes: &[usize], newpos: &'a[Vector3D]) -> &'a Vector3D {
    for (idx, &particle) in idxes.iter().enumerate() {
        if particle == i {
            return &newpos[idx];
        }
    }
    return &system[i].position;
}

#[cfg(test)]
mod tests {
    use super::*;
    use sys::{System, UnitCell};
    use energy::*;
    use utils::{system_from_xyz, unit_from};
    use types::Vector3D;

    fn testing_system() -> System {
        let mut system = system_from_xyz("4
        bonds
        O     0.000000     0.000000     0.000000
        O     0.000000     0.000000     1.480000
        H     0.895669     0.000000    -0.316667
        H    -0.895669     0.000000     1.796667");
        system.set_cell(UnitCell::cubic(10.0));
        assert!(system.molecules().len() == 1);

        system.interactions_mut().add_pair("H", "H", PairInteraction::new(
            Box::new(LennardJones{sigma: 3.0, epsilon: unit_from(0.5, "kJ/mol")}), 3.0
        ));

        system.interactions_mut().add_pair("O", "O", PairInteraction::new(
            Box::new(NullPotential), 3.0
        ));

        system.interactions_mut().add_pair("O", "H", PairInteraction::new(
            Box::new(LennardJones{sigma: 1.0, epsilon: unit_from(0.3, "kJ/mol")}), 3.0
        ));

        system.interactions_mut().add_bond("O", "H",
            Box::new(Harmonic{x0: 3.4, k: unit_from(522.0, "kJ/mol/A^2")})
        );

        system.interactions_mut().add_angle("O", "O", "H",
            Box::new(Harmonic{x0: f64::to_radians(120.0), k: unit_from(150.0, "kJ/mol/deg^2")})
        );

        system.interactions_mut().add_dihedral("H", "O", "O", "H",
            Box::new(Harmonic{x0: f64::to_radians(180.0), k: unit_from(800.0, "kJ/mol/deg^2")})
        );

        system.interactions_mut().set_coulomb(Box::new(Wolf::new(8.0)));

        for atom in &mut system {
            if atom.name() == "O" {
                atom.charge = -0.5;
            } else if atom.name() == "H" {
                atom.charge = 0.5;
            }
        }

        return system;
    }

    #[test]
    fn cache_energy() {
        let system = testing_system();
        let mut cache = EnergyCache::new();
        cache.init(&system);

        assert_approx_eq!(cache.energy(), system.potential_energy());
    }

    #[test]
    fn move_atoms() {
        let mut system = testing_system();
        let mut cache = EnergyCache::new();
        let old_e = system.potential_energy();
        cache.init(&system);

        let idxes = vec![0, 3];
        let newpos = &[Vector3D::new(0.0, 0.0, 0.5), Vector3D::new(-0.7, 0.2, 1.5)];

        let cost = cache.move_particles_cost(&system, idxes, newpos);

        system[0].position = newpos[0];
        system[3].position = newpos[1];
        let new_e = system.potential_energy();
        assert_approx_eq!(cost, new_e - old_e, 1e-14);

        cache.update(&mut system);
        assert_approx_eq!(cache.energy(), new_e, 1e-14);

        // Check that the cache is really updated
        let old_e = new_e;
        let idxes = vec![2, 3];
        let newpos = &[Vector3D::new(0.9, 0.2, -0.4), Vector3D::new(-0.9, 0.0, 1.8)];
        let cost = cache.move_particles_cost(&system, idxes, newpos);
        system[2].position = newpos[0];
        system[3].position = newpos[1];
        let new_e = system.potential_energy();
        assert_approx_eq!(cost, new_e - old_e, 1e-14);
    }
}
<|endoftext|>


<commit_before><commit_msg>Start on pattern-matching bits<commit_after><|endoftext|>


<commit_before>// Not public API.
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! punct_len {
    ($mode:ident, +)     => { 1 };
    ($mode:ident, +=)    => { 2 };
    ($mode:ident, &)     => { 1 };
    ($mode:ident, &&)    => { 2 };
    ($mode:ident, &=)    => { 2 };
    ($mode:ident, @)     => { 1 };
    ($mode:ident, !)     => { 1 };
    ($mode:ident, ^)     => { 1 };
    ($mode:ident, ^=)    => { 2 };
    ($mode:ident, :)     => { 1 };
    ($mode:ident, ::)    => { 2 };
    ($mode:ident, ,)     => { 1 };
    ($mode:ident, /)     => { 1 };
    ($mode:ident, /=)    => { 2 };
    ($mode:ident, .)     => { 1 };
    ($mode:ident, ..)    => { 2 };
    ($mode:ident, ...)   => { 3 };
    ($mode:ident, ..=)   => { 3 };
    ($mode:ident, =)     => { 1 };
    ($mode:ident, ==)    => { 2 };
    ($mode:ident, >=)    => { 2 };
    ($mode:ident, >)     => { 1 };
    ($mode:ident, <=)    => { 2 };
    ($mode:ident, <)     => { 1 };
    ($mode:ident, *=)    => { 2 };
    ($mode:ident, !=)    => { 2 };
    ($mode:ident, |)     => { 1 };
    ($mode:ident, |=)    => { 2 };
    ($mode:ident, ||)    => { 2 };
    ($mode:ident, #)     => { 1 };
    ($mode:ident, ?)     => { 1 };
    ($mode:ident, ->)    => { 2 };
    ($mode:ident, <-)    => { 2 };
    ($mode:ident, %)     => { 1 };
    ($mode:ident, %=)    => { 2 };
    ($mode:ident, =>)    => { 2 };
    ($mode:ident, ;)     => { 1 };
    ($mode:ident, <<)    => { 2 };
    ($mode:ident, <<=)   => { 3 };
    ($mode:ident, >>)    => { 2 };
    ($mode:ident, >>=)   => { 3 };
    ($mode:ident, *)     => { 1 };
    ($mode:ident, -)     => { 1 };
    ($mode:ident, -=)    => { 2 };
    ($mode:ident, ~)     => { 1 };
    (lenient, $tt:tt)    => { 0 };
    (strict, $tt:tt)     => {{ unexpected!($tt); 0 }};
    ($mode:ident, $head:tt $($tail:tt)+) => {
        punct_len!($mode, $head) $(+ punct_len!($mode, $tail))+
    };
}

// Not public API.
#[doc(hidden)]
#[macro_export]
macro_rules! unexpected {
    () => {};
}

// Not public API.
#[doc(hidden)]
#[macro_export]
macro_rules! stringify_punct {
    ($($tt:tt)+) => {
        concat!($(stringify!($tt)),+)
    };
}

// Not public API.
// Without this, local_inner_macros breaks when looking for concat!
#[doc(hidden)]
#[macro_export]
macro_rules! my_concat {
    ($($tt:tt)*) => {
        concat!($($tt)*)
    };
}

/// Define a type that supports parsing and printing a multi-character symbol
/// as if it were a token.
///
/// # Usage
///
/// As a convention, it is recommended that this macro be invoked within a
/// module called `punct` or `punctuation` and that the resulting parser be invoked
/// with a `punct::` or `punctuation::` prefix.
///
/// ```edition2018
/// mod punct {
///     syn::custom_punctuation!(LeftRightArrow, <=>);
/// }
/// ```
///
/// The generated syntax tree node supports the following operations just like
/// any built-in punctuation token.
///
/// - [Peeking] — `input.peek(punct::LeftRightArrow)`
///
/// - [Parsing] — `input.parse::<punct::LeftRightArrow>()?`
///
/// - [Printing] — `quote!( ... #left_right_arrow ... )`
///
/// - Construction from a [`Span`] — `let left_right_arrow = punct::LeftRightArrow(sp)`
///
/// - Construction from multiple [`Span`] — `let left_right_arrow = punct::LeftRightArrow([sp, sp, sp])`
///
/// - Field access to its spans — `let spans = left_right_arrow.spans`
///
/// [Peeking]: parse/struct.ParseBuffer.html#method.peek
/// [Parsing]: parse/struct.ParseBuffer.html#method.parse
/// [Printing]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
/// [`Span`]: struct.Span.html
///
/// # Example
///
/// ```edition2018
/// use syn::{Expr, ExprParen, parenthesized};
/// use syn::punctuated::Punctuated;
/// use syn::parse::{Parse, ParseStream, Result};
/// use syn::token::Paren;
///
/// mod punct {
///     syn::custom_punctuation!(PathSeparator, </>);
/// }
///
/// // (expr) </> (expr) </> (expr) ...
/// struct PathSegments {
///     segments: Punctuated<Expr, punct::PathSeparator>,
/// }
///
/// impl Parse for PathSegments {
///     fn parse(input: ParseStream) -> Result<Self> {
///         let mut segments = Punctuated::new();
///
///         let la = input.lookahead1();
///         if la.peek(Paren) {
///             let content;
///             let paren_token = parenthesized!(content in input);
///             let expr = Box::new(content.parse()?);
///             segments.push_value(Expr::Paren(ExprParen { attrs: vec![], paren_token, expr }));
///         } else {
///             return Err(la.error());
///         }
///
///         while input.peek(punct::PathSeparator) {
///             segments.push_punct(input.parse()?);
///             let content;
///             let paren_token = parenthesized!(content in input);
///             let expr = Box::new(content.parse()?);
///             segments.push_value(Expr::Paren(ExprParen { attrs: vec![], paren_token, expr }));
///         }
///
///         Ok(PathSegments { segments })
///     }
/// }
///
/// let _: PathSegments = syn::parse_str(r#"("five") </> ("hundred")"#).unwrap();
/// ```
///
#[macro_export(local_inner_macros)]
macro_rules! custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        pub struct $ident {
            pub spans: [$crate::export::Span; punct_len!(lenient, $($tt)+)],
        }

        #[doc(hidden)]
        #[allow(non_snake_case)]
        pub fn $ident<__S: $crate::export::IntoSpans<[$crate::export::Span; punct_len!(lenient, $($tt)+)]>>(
            spans: __S,
        ) -> $ident {
            let _punct_len = punct_len!(strict, $($tt)+);
            $ident {
                spans: $crate::export::IntoSpans::into_spans(spans)
            }
        }

        impl $crate::export::Default for $ident {
            fn default() -> Self {
                $ident($crate::export::Span::call_site())
            }
        }

        impl_parse_for_custom_punctuation!($ident, $($tt)+);
        impl_to_tokens_for_custom_punctuation!($ident, $($tt)+);
        impl_clone_for_custom_punctuation!($ident, $($tt)+);
        impl_extra_traits_for_custom_punctuation!($ident, $($tt)+);
    };
}

// Not public API.
#[cfg(feature = "parsing")]
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_parse_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        impl $crate::token::CustomToken for $ident {
            fn peek(cursor: $crate::buffer::Cursor) -> bool {
                $crate::token::parsing::peek_punct(cursor, stringify_punct!($($tt)+))
            }

            fn display() -> &'static $crate::export::str {
                my_concat!("`", stringify_punct!($($tt)+), "`")
            }
        }

        impl $crate::parse::Parse for $ident {
            fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> {
                let spans: [$crate::export::Span; punct_len!(lenient, $($tt)+)] =
                    $crate::token::parsing::punct(input, stringify_punct!($($tt)+))?;
                Ok($ident(spans))
            }
        }
    };
}

// Not public API.
#[cfg(not(feature = "parsing"))]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_parse_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {};
}

// Not public API.
#[cfg(feature = "printing")]
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_to_tokens_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        impl $crate::export::ToTokens for $ident {
            fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) {
                $crate::token::printing::punct(stringify_punct!($($tt)+), &self.spans, tokens)
            }
        }
    };
}

// Not public API.
#[cfg(not(feature = "printing"))]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_to_tokens_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {};
}

// Not public API.
#[cfg(feature = "clone-impls")]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_clone_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        impl $crate::export::Copy for $ident {}

        impl $crate::export::Clone for $ident {
            fn clone(&self) -> Self {
                *self
            }
        }
    };
}

// Not public API.
#[cfg(not(feature = "clone-impls"))]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_clone_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {};
}

// Not public API.
#[cfg(feature = "extra-traits")]
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_extra_traits_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        impl $crate::export::Debug for $ident {
            fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result {
                $crate::export::Formatter::write_str(f, stringify_punct!($($tt)+))
            }
        }

        impl $crate::export::Eq for $ident {}

        impl $crate::export::PartialEq for $ident {
            fn eq(&self, _other: &Self) -> $crate::export::bool {
                true
            }
        }

        impl $crate::export::Hash for $ident {
            fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {}
        }
    };
}

// Not public API.
#[cfg(not(feature = "extra-traits"))]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_extra_traits_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {};
}
<commit_msg>Move custom_punctuation implementation details to bottom<commit_after>/// Define a type that supports parsing and printing a multi-character symbol
/// as if it were a token.
///
/// # Usage
///
/// As a convention, it is recommended that this macro be invoked within a
/// module called `punct` or `punctuation` and that the resulting parser be invoked
/// with a `punct::` or `punctuation::` prefix.
///
/// ```edition2018
/// mod punct {
///     syn::custom_punctuation!(LeftRightArrow, <=>);
/// }
/// ```
///
/// The generated syntax tree node supports the following operations just like
/// any built-in punctuation token.
///
/// - [Peeking] — `input.peek(punct::LeftRightArrow)`
///
/// - [Parsing] — `input.parse::<punct::LeftRightArrow>()?`
///
/// - [Printing] — `quote!( ... #left_right_arrow ... )`
///
/// - Construction from a [`Span`] — `let left_right_arrow = punct::LeftRightArrow(sp)`
///
/// - Construction from multiple [`Span`] — `let left_right_arrow = punct::LeftRightArrow([sp, sp, sp])`
///
/// - Field access to its spans — `let spans = left_right_arrow.spans`
///
/// [Peeking]: parse/struct.ParseBuffer.html#method.peek
/// [Parsing]: parse/struct.ParseBuffer.html#method.parse
/// [Printing]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
/// [`Span`]: struct.Span.html
///
/// # Example
///
/// ```edition2018
/// use syn::{Expr, ExprParen, parenthesized};
/// use syn::punctuated::Punctuated;
/// use syn::parse::{Parse, ParseStream, Result};
/// use syn::token::Paren;
///
/// mod punct {
///     syn::custom_punctuation!(PathSeparator, </>);
/// }
///
/// // (expr) </> (expr) </> (expr) ...
/// struct PathSegments {
///     segments: Punctuated<Expr, punct::PathSeparator>,
/// }
///
/// impl Parse for PathSegments {
///     fn parse(input: ParseStream) -> Result<Self> {
///         let mut segments = Punctuated::new();
///
///         let la = input.lookahead1();
///         if la.peek(Paren) {
///             let content;
///             let paren_token = parenthesized!(content in input);
///             let expr = Box::new(content.parse()?);
///             segments.push_value(Expr::Paren(ExprParen { attrs: vec![], paren_token, expr }));
///         } else {
///             return Err(la.error());
///         }
///
///         while input.peek(punct::PathSeparator) {
///             segments.push_punct(input.parse()?);
///             let content;
///             let paren_token = parenthesized!(content in input);
///             let expr = Box::new(content.parse()?);
///             segments.push_value(Expr::Paren(ExprParen { attrs: vec![], paren_token, expr }));
///         }
///
///         Ok(PathSegments { segments })
///     }
/// }
///
/// let _: PathSegments = syn::parse_str(r#"("five") </> ("hundred")"#).unwrap();
/// ```
///
#[macro_export(local_inner_macros)]
macro_rules! custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        pub struct $ident {
            pub spans: [$crate::export::Span; punct_len!(lenient, $($tt)+)],
        }

        #[doc(hidden)]
        #[allow(non_snake_case)]
        pub fn $ident<__S: $crate::export::IntoSpans<[$crate::export::Span; punct_len!(lenient, $($tt)+)]>>(
            spans: __S,
        ) -> $ident {
            let _punct_len = punct_len!(strict, $($tt)+);
            $ident {
                spans: $crate::export::IntoSpans::into_spans(spans)
            }
        }

        impl $crate::export::Default for $ident {
            fn default() -> Self {
                $ident($crate::export::Span::call_site())
            }
        }

        impl_parse_for_custom_punctuation!($ident, $($tt)+);
        impl_to_tokens_for_custom_punctuation!($ident, $($tt)+);
        impl_clone_for_custom_punctuation!($ident, $($tt)+);
        impl_extra_traits_for_custom_punctuation!($ident, $($tt)+);
    };
}

// Not public API.
#[cfg(feature = "parsing")]
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_parse_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        impl $crate::token::CustomToken for $ident {
            fn peek(cursor: $crate::buffer::Cursor) -> bool {
                $crate::token::parsing::peek_punct(cursor, stringify_punct!($($tt)+))
            }

            fn display() -> &'static $crate::export::str {
                my_concat!("`", stringify_punct!($($tt)+), "`")
            }
        }

        impl $crate::parse::Parse for $ident {
            fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> {
                let spans: [$crate::export::Span; punct_len!(lenient, $($tt)+)] =
                    $crate::token::parsing::punct(input, stringify_punct!($($tt)+))?;
                Ok($ident(spans))
            }
        }
    };
}

// Not public API.
#[cfg(not(feature = "parsing"))]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_parse_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {};
}

// Not public API.
#[cfg(feature = "printing")]
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_to_tokens_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        impl $crate::export::ToTokens for $ident {
            fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) {
                $crate::token::printing::punct(stringify_punct!($($tt)+), &self.spans, tokens)
            }
        }
    };
}

// Not public API.
#[cfg(not(feature = "printing"))]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_to_tokens_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {};
}

// Not public API.
#[cfg(feature = "clone-impls")]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_clone_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        impl $crate::export::Copy for $ident {}

        impl $crate::export::Clone for $ident {
            fn clone(&self) -> Self {
                *self
            }
        }
    };
}

// Not public API.
#[cfg(not(feature = "clone-impls"))]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_clone_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {};
}

// Not public API.
#[cfg(feature = "extra-traits")]
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_extra_traits_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {
        impl $crate::export::Debug for $ident {
            fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result {
                $crate::export::Formatter::write_str(f, stringify_punct!($($tt)+))
            }
        }

        impl $crate::export::Eq for $ident {}

        impl $crate::export::PartialEq for $ident {
            fn eq(&self, _other: &Self) -> $crate::export::bool {
                true
            }
        }

        impl $crate::export::Hash for $ident {
            fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {}
        }
    };
}

// Not public API.
#[cfg(not(feature = "extra-traits"))]
#[doc(hidden)]
#[macro_export]
macro_rules! impl_extra_traits_for_custom_punctuation {
    ($ident:ident, $($tt:tt)+) => {};
}

// Not public API.
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! punct_len {
    ($mode:ident, +)     => { 1 };
    ($mode:ident, +=)    => { 2 };
    ($mode:ident, &)     => { 1 };
    ($mode:ident, &&)    => { 2 };
    ($mode:ident, &=)    => { 2 };
    ($mode:ident, @)     => { 1 };
    ($mode:ident, !)     => { 1 };
    ($mode:ident, ^)     => { 1 };
    ($mode:ident, ^=)    => { 2 };
    ($mode:ident, :)     => { 1 };
    ($mode:ident, ::)    => { 2 };
    ($mode:ident, ,)     => { 1 };
    ($mode:ident, /)     => { 1 };
    ($mode:ident, /=)    => { 2 };
    ($mode:ident, .)     => { 1 };
    ($mode:ident, ..)    => { 2 };
    ($mode:ident, ...)   => { 3 };
    ($mode:ident, ..=)   => { 3 };
    ($mode:ident, =)     => { 1 };
    ($mode:ident, ==)    => { 2 };
    ($mode:ident, >=)    => { 2 };
    ($mode:ident, >)     => { 1 };
    ($mode:ident, <=)    => { 2 };
    ($mode:ident, <)     => { 1 };
    ($mode:ident, *=)    => { 2 };
    ($mode:ident, !=)    => { 2 };
    ($mode:ident, |)     => { 1 };
    ($mode:ident, |=)    => { 2 };
    ($mode:ident, ||)    => { 2 };
    ($mode:ident, #)     => { 1 };
    ($mode:ident, ?)     => { 1 };
    ($mode:ident, ->)    => { 2 };
    ($mode:ident, <-)    => { 2 };
    ($mode:ident, %)     => { 1 };
    ($mode:ident, %=)    => { 2 };
    ($mode:ident, =>)    => { 2 };
    ($mode:ident, ;)     => { 1 };
    ($mode:ident, <<)    => { 2 };
    ($mode:ident, <<=)   => { 3 };
    ($mode:ident, >>)    => { 2 };
    ($mode:ident, >>=)   => { 3 };
    ($mode:ident, *)     => { 1 };
    ($mode:ident, -)     => { 1 };
    ($mode:ident, -=)    => { 2 };
    ($mode:ident, ~)     => { 1 };
    (lenient, $tt:tt)    => { 0 };
    (strict, $tt:tt)     => {{ unexpected!($tt); 0 }};
    ($mode:ident, $head:tt $($tail:tt)+) => {
        punct_len!($mode, $head) $(+ punct_len!($mode, $tail))+
    };
}

// Not public API.
#[doc(hidden)]
#[macro_export]
macro_rules! unexpected {
    () => {};
}

// Not public API.
#[doc(hidden)]
#[macro_export]
macro_rules! stringify_punct {
    ($($tt:tt)+) => {
        concat!($(stringify!($tt)),+)
    };
}

// Not public API.
// Without this, local_inner_macros breaks when looking for concat!
#[doc(hidden)]
#[macro_export]
macro_rules! my_concat {
    ($($tt:tt)*) => {
        concat!($($tt)*)
    };
}
<|endoftext|>


<commit_before>// Copyright 2017 Serde Developers
//
// 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.

#![feature(lang_items, start)]
#![no_std]

extern crate libc;

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    0
}

#[lang = "eh_personality"]
#[no_mangle]
pub extern "C" fn rust_eh_personality() {}

#[lang = "panic_impl"]
fn panic_impl(_info: &core::panic::PanicInfo) -> ! {
    unsafe {
        libc::abort();
    }
}

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

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
struct Unit;

#[derive(Serialize, Deserialize)]
struct Newtype(u8);

#[derive(Serialize, Deserialize)]
struct Tuple(u8, u8);

#[derive(Serialize, Deserialize)]
struct Struct {
    f: u8,
}

#[derive(Serialize, Deserialize)]
enum Enum {
    Unit,
    Newtype(u8),
    Tuple(u8, u8),
    Struct { f: u8 },
}
<commit_msg>Switch no-std panic to #[panic_implementation]<commit_after>// Copyright 2017 Serde Developers
//
// 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.

#![feature(lang_items, start, panic_implementation)]
#![no_std]

extern crate libc;

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    0
}

#[lang = "eh_personality"]
#[no_mangle]
pub extern "C" fn rust_eh_personality() {}

#[panic_implementation]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    unsafe {
        libc::abort();
    }
}

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

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
struct Unit;

#[derive(Serialize, Deserialize)]
struct Newtype(u8);

#[derive(Serialize, Deserialize)]
struct Tuple(u8, u8);

#[derive(Serialize, Deserialize)]
struct Struct {
    f: u8,
}

#[derive(Serialize, Deserialize)]
enum Enum {
    Unit,
    Newtype(u8),
    Tuple(u8, u8),
    Struct { f: u8 },
}
<|endoftext|>


<commit_before>use vkffi::*;
use render_vk::wrap as vk;
use std;
use traits::*;
use device_resources;
use rand; use time;
use rand::distributions::*;

const MAX_BK_COUNT: VkDeviceSize = 64;

#[repr(C)]
struct BackgroundInstance
{
	offset: [f32; 4], scale: [f32; 4]
}
#[repr(C)]
struct UniformBufferData
{
	instances: [BackgroundInstance; MAX_BK_COUNT as usize]
}

pub struct BackgroundDatastore
{
	#[allow(dead_code)] descriptor_set_index: usize,
	pub uniform_offset: VkDeviceSize, pub index_multipliers_offset: VkDeviceSize,
	descriptor_buffer_info: VkDescriptorBufferInfo
}
impl BackgroundDatastore
{
	pub fn new<'d>(buffer: &vk::Buffer<'d>, offset: VkDeviceSize, descriptor_set_index: usize) -> BackgroundDatastore
	{
		BackgroundDatastore
		{
			descriptor_set_index: descriptor_set_index,
			uniform_offset: offset,
			index_multipliers_offset: offset + Self::required_sizes()[0],
			descriptor_buffer_info: VkDescriptorBufferInfo(**buffer, offset, Self::required_sizes()[0])
		}
	}
	pub fn update(&self, mapped_range: &vk::MemoryMappedRange, mut randomizer: &mut rand::Rng, delta_time: time::Duration)
	{
		let delta_ms = delta_time.num_microseconds().unwrap_or(0) as f32 / 1000.0f32;
		let uniform_range = mapped_range.map_mut::<UniformBufferData>(self.uniform_offset);
		let index_multipliers_range = mapped_range.map_mut::<[u32; MAX_BK_COUNT as usize]>(self.index_multipliers_offset);
		let mut rrange = rand::distributions::Range::new(0, 64 * 64);
		let mut left_range = rand::distributions::Range::new(-10.0f32, 10.0f32);
		let mut count_range = rand::distributions::Range::new(2, 10);
		let mut scale_range = rand::distributions::Range::new(1.0f32, 3.0f32);
		for i in 0 .. MAX_BK_COUNT as usize
		{
			if index_multipliers_range[i] == 0
			{
				// instantiate randomly
				if rrange.sample(&mut randomizer) == 0
				{
					let scale = scale_range.sample(&mut randomizer);
					index_multipliers_range[i] = 1;
					uniform_range.instances[i].offset = [left_range.sample(&mut randomizer), -20.0f32, -20.0f32, count_range.sample(&mut randomizer) as f32];
					uniform_range.instances[i].scale = [scale, scale, 1.0f32, 1.0f32];
				}
			}
			else
			{
				uniform_range.instances[i].offset[1] += delta_ms / 32.0f32;
				if uniform_range.instances[i].offset[1] >= 20.0f32
				{
					index_multipliers_range[i] = 0;
				}
			}
		}
	}
}
impl DeviceStore for BackgroundDatastore
{
	fn required_sizes() -> Vec<VkDeviceSize>
	{
		vec![std::mem::size_of::<UniformBufferData>() as VkDeviceSize, std::mem::size_of::<u32>() as VkDeviceSize * MAX_BK_COUNT as VkDeviceSize]
	}
	fn initial_stage_data(&self, mapped_range: &vk::MemoryMappedRange)
	{
		let index_multipliers_range = mapped_range.map_mut::<[u32; MAX_BK_COUNT as usize]>(self.index_multipliers_offset);

		*index_multipliers_range = [0u32; MAX_BK_COUNT as usize];
	}
}
impl HasDescriptor for BackgroundDatastore
{
	fn write_descriptor_info<'d>(&self, sets: &device_resources::DescriptorSets<'d>) -> Vec<VkWriteDescriptorSet>
	{
		vec![VkWriteDescriptorSet
		{
			sType: VkStructureType::WriteDescriptorSet, pNext: std::ptr::null(),
			dstSet: sets.sets[self.descriptor_set_index], dstBinding: 0, dstArrayElement: 0,
			descriptorType: VkDescriptorType::UniformBuffer, descriptorCount: 1,
			pBufferInfo: &self.descriptor_buffer_info, pImageInfo: std::ptr::null(), pTexelBufferView: std::ptr::null()
		}]
	}
}
<commit_msg>changed delta calcuation to use seconds<commit_after>use vkffi::*;
use render_vk::wrap as vk;
use std;
use traits::*;
use device_resources;
use rand; use time;
use rand::distributions::*;

const MAX_BK_COUNT: VkDeviceSize = 64;

#[repr(C)]
struct BackgroundInstance
{
	offset: [f32; 4], scale: [f32; 4]
}
#[repr(C)]
struct UniformBufferData
{
	instances: [BackgroundInstance; MAX_BK_COUNT as usize]
}

pub struct BackgroundDatastore
{
	#[allow(dead_code)] descriptor_set_index: usize,
	pub uniform_offset: VkDeviceSize, pub index_multipliers_offset: VkDeviceSize,
	descriptor_buffer_info: VkDescriptorBufferInfo
}
impl BackgroundDatastore
{
	pub fn new<'d>(buffer: &vk::Buffer<'d>, offset: VkDeviceSize, descriptor_set_index: usize) -> BackgroundDatastore
	{
		BackgroundDatastore
		{
			descriptor_set_index: descriptor_set_index,
			uniform_offset: offset,
			index_multipliers_offset: offset + Self::required_sizes()[0],
			descriptor_buffer_info: VkDescriptorBufferInfo(**buffer, offset, Self::required_sizes()[0])
		}
	}
	pub fn update(&self, mapped_range: &vk::MemoryMappedRange, mut randomizer: &mut rand::Rng, delta_time: time::Duration)
	{
		let delta_sec = delta_time.num_microseconds().unwrap_or(0) as f32 / (1000.0f32 * 1000.0f32);
		let uniform_range = mapped_range.map_mut::<UniformBufferData>(self.uniform_offset);
		let index_multipliers_range = mapped_range.map_mut::<[u32; MAX_BK_COUNT as usize]>(self.index_multipliers_offset);
		let mut rrange = rand::distributions::Range::new(0, 64 * 4);
		let mut left_range = rand::distributions::Range::new(-10.0f32, 10.0f32);
		let mut count_range = rand::distributions::Range::new(2, 10);
		let mut scale_range = rand::distributions::Range::new(1.0f32, 3.0f32);
		for i in 0 .. MAX_BK_COUNT as usize
		{
			if index_multipliers_range[i] == 0
			{
				// instantiate randomly
				if rrange.sample(&mut randomizer) == 0
				{
					let scale = scale_range.sample(&mut randomizer);
					index_multipliers_range[i] = 1;
					uniform_range.instances[i].offset = [left_range.sample(&mut randomizer), -20.0f32, -20.0f32, count_range.sample(&mut randomizer) as f32];
					uniform_range.instances[i].scale = [scale, scale, 1.0f32, 1.0f32];
				}
			}
			else
			{
				uniform_range.instances[i].offset[1] += delta_sec * 16.0f32;
				if uniform_range.instances[i].offset[1] >= 20.0f32
				{
					index_multipliers_range[i] = 0;
				}
			}
		}
	}
}
impl DeviceStore for BackgroundDatastore
{
	fn required_sizes() -> Vec<VkDeviceSize>
	{
		vec![std::mem::size_of::<UniformBufferData>() as VkDeviceSize, std::mem::size_of::<u32>() as VkDeviceSize * MAX_BK_COUNT as VkDeviceSize]
	}
	fn initial_stage_data(&self, mapped_range: &vk::MemoryMappedRange)
	{
		let index_multipliers_range = mapped_range.map_mut::<[u32; MAX_BK_COUNT as usize]>(self.index_multipliers_offset);

		*index_multipliers_range = [0u32; MAX_BK_COUNT as usize];
	}
}
impl HasDescriptor for BackgroundDatastore
{
	fn write_descriptor_info<'d>(&self, sets: &device_resources::DescriptorSets<'d>) -> Vec<VkWriteDescriptorSet>
	{
		vec![VkWriteDescriptorSet
		{
			sType: VkStructureType::WriteDescriptorSet, pNext: std::ptr::null(),
			dstSet: sets.sets[self.descriptor_set_index], dstBinding: 0, dstArrayElement: 0,
			descriptorType: VkDescriptorType::UniformBuffer, descriptorCount: 1,
			pBufferInfo: &self.descriptor_buffer_info, pImageInfo: std::ptr::null(), pTexelBufferView: std::ptr::null()
		}]
	}
}
<|endoftext|>


<commit_before>//! Interpolation functions and derivatives for geometric shapes (elements)
//!
//! **Note:** The [Shape] struct is the main component of this module whereas the Hex8,
//! Qua4, Lin2, etc. are secondary components.
//!
//! The [Shape] struct, after allocation, should be treated as immutable, although its
//! members are public (readonly). On the other hand the [StateOfShape] contains the
//! mutable data required by [Shape] to perform the computations explained below.
//!
//! # Examples
//!
//! See [Shape].
//!
//! # Definitions
//!
//! Here, we consider the following definitions:
//!
//! * `space_ndim` -- is the number of dimensions of the space under study (2 or 3)
//! * `geo_ndim` -- is the number of dimensions of the geometry element (shape).
//!                  For instance, a line in the 2D space has `geo_ndim = 1` and
//!                  `space_ndim = 2`. Another example: a triangle in the 3D space
//!                  has `geo_ndim = 2` and `space_ndim = 3`.
//! * `local` -- refers to a numbering scheme for the nodes of the shape (or element)
//! * `global` -- refers to a numbering scheme applied for the whole mesh
//! * `spatial (real) space` -- is the "real" space mapped by the x₀,x₁,x₂ coordinates (see figure below)
//! * `reference (natural) space` -- is the "virtual" space mapped by the ξ₀,ξ₁,ξ₂ coordinates (see figure below)
//!
//! **Note:** In the code, we use (r,s,t) as (ξ₀,ξ₁,ξ₂) and `ksi` to represent the vector `ξ`.
//!
//! ![Real and reference space mapping](https://github.com/cpmech/gemlab/raw/main/data/figures/mapping-real-to-reference.png)
//!
//! We also consider the following counting variables:
//!
//! * `nnode` -- (local) number of points (aka nodes) that define the shape/element.
//! * `npoint` -- (global) number of points in the whole mesh; not used in this module
//!               but important to remember
//! * `nedge` -- number of edges on the shape (2D or 3D)
//! * `nface` -- number of faces on the shape (3D only)
//! * `edge_nnode` -- number of points/nodes that define the edge
//! * `face_nnode` -- number of points/nodes that define the face
//! * `face_nedge` -- number of edges on the face
//!
//! # Isoparametric formulation
//!
//! The isoparametric formulation establishes that we can calculate the coordinates `x(ξ)`
//! within the shape/element from the shape functions `Nᵐ(ξ)` and the coordinates at each
//! node by using the formula:
//!
//! ```text
//! → →         →  →
//! x(ξ) = Σ Nᵐ(ξ) xᵐ
//!        m
//! ```
//!
//! where `x` is the (space_ndim) vector of real coordinates, `ξ` is the (geo_ndim)
//! vector of reference coordinates, `Nᵐ` are the (nnode) interpolation functions,
//! and `xᵐ` are the (nnode) coordinates of each m-node of the geometric shape.
//!
//! Given an (nnode,space_ndim) **matrix** of coordinates `X`, we can calculate the
//! (space_ndim) **vector** of coordinates `x` by means of
//!
//! ```text
//! x = Xᵀ ⋅ N
//! ```
//!
//! where `N` is an (nnode) **vector** formed with all `Nᵐ` values.
//!
//! # Derivatives on the reference space and gradients on the real space
//!
//! Here, we consider two cases:
//!
//! * General case with geo_ndim == space_ndim; and
//! * Line in multi-dimensions with geo_ndim = 1 and space_ndim > 1.
//!
//! ## General case with geo_ndim == space_ndim
//!
//! If `geo_ndim == space_ndim`, we define the Jacobian tensor as
//!
//! ```text
//!         →
//!   →    dx     →    →
//! J(ξ) = —— = Σ xᵐ ⊗ Lᵐ
//!         →   m
//!        dξ
//! ```
//!
//! where
//!
//! ```text
//!             →
//! →  →    dNᵐ(ξ)
//! Lᵐ(ξ) = ——————
//!            →
//!           dξ
//! ```
//!
//! are the derivatives of each interpolation function `Nᵐ` with respect to the
//! reference coordinate. `Lᵐ` are (geo_ndim) vectors and can be organized in
//! an (nnode,geo_ndim) matrix `L` of **local** derivatives.
//!
//! We can write the Jacobian in matrix notation as follows
//!
//! ```text
//! J = Xᵀ · L
//! ```
//!
//! where `X` is the (nnode,space_ndim) matrix of coordinates and `L` is the (nnode,geo_ndim) matrix.
//!
//! Next, we define the gradient of interpolation functions (i.e., derivatives of interpolation
//! functions with respect to real coordinates) by means of
//!
//! ```text
//!             →
//! →  →    dNᵐ(ξ)
//! Gᵐ(ξ) = ——————
//!            →
//!           dx
//! ```
//!
//! which can be organized in an (nnode,space_ndim) matrix `G`.
//!
//! The inverse Jacobian allows us to determine the gradient vectors `G` as follows
//!
//! ```text
//! →       →  →        →
//! Gᵐ(ξ) = Lᵐ(ξ) · J⁻¹(ξ)
//! ```
//!
//! Or, in matrix notation,
//!
//! ```text
//! G = L · J⁻¹
//! ```
//!
//! where `G` is an (nnode,space_ndim) matrix.
//!
//! ## Line in multi-dimensions (geo_ndim = 1 and space_ndim > 1)
//!
//! In this case, the Jacobian equals the (space_ndim,1) base vector `g₁` which
//! is tangent to the line element, i.e.,
//!
//! ```text
//!                          →
//! →    →     →            dx
//! J := Jline(ξ) = g₁(ξ) = —— = Xᵀ · L
//!                         dξ
//! ```
//!
//! We also consider a parametric coordinate `ℓ` which varies
//! from `0` to `ℓ_max` (the length of the line) according to
//!
//! ```text
//!                ℓ_max
//! ℓ(ξ) = (1 + ξ) —————
//!                  2
//!
//!        2 · ℓ
//! ξ(ℓ) = ————— - 1
//!        ℓ_max
//! ```
//!
//! Note that:
//!
//! ```text
//! 0 ≤ ℓ ≤ ℓ_max
//!
//! -1 ≤ ξ ≤ +1
//! ```
//!
//! # Normal vectors
//!
//! ## Line in multi-dimensions (geo_ndim = 1 and space_ndim > 1)
//!
//! Base vector tangent to the line:
//!
//! ```text
//!          →
//!         dx
//! g₁(ξ) = —— = Xᵀ · L = first_column(J)
//!         dξ
//! ```
//!
//! Normal vector:
//!
//! ```text
//! →   →    →
//! n = e₃ × g₁
//!
//!   →       →
//! ||n|| = ||g₁||
//! ```
//!
//! Thus
//!
//! ```text
//!        →           →
//! dℓ = ||g₁|| dξ = ||n|| dξ
//! ```
//!
//! For a straight line (segment):
//!
//! ```text
//!   →
//! ||n|| = Δℓ / Δξ = L / 2
//! ```
//!
//! because all [GeoClass::Lin] have `Δξ = 2`.
//!
//! ## Boundary surface (geo_ndim = 2 and space_ndim = 3)
//!
//! Base vectors tangent to the surface:
//!
//! ```text
//!          →
//! →  →    dx
//! g₁(ξ) = ——— = first_column(J)
//!         dξ₁
//!
//!          →
//! →  →    dx
//! g₂(ξ) = ——— = second_column(J)
//!         dξ₂
//! ```
//!
//! Normal vector:
//!
//! ```text
//! →   →    →
//! n = g₁ × g₂
//! ```
//!
//! Thus
//!
//! ```text
//!         →
//! dA := ||n|| dξ₁ dξ₂
//! ```
//!
//! For flat quadrilateral faces:
//!
//! ```text
//!   →
//! ||n|| = A / (Δξ₁ Δξ₂) = A / 4
//! ```
//!
//! because all [GeoClass::Qua] have `Δξᵢ = 2`.
//!

mod enums;
mod hex20;
mod hex32;
mod hex8;
mod lin2;
mod lin3;
mod lin4;
mod lin5;
pub mod op;
mod qua12;
mod qua16;
mod qua17;
mod qua4;
mod qua8;
mod qua9;
mod scratchpad;
mod shape;
mod state_of_shape;
mod tet10;
mod tet20;
mod tet4;
mod tri10;
mod tri15;
mod tri3;
mod tri6;
pub use crate::shapes::enums::*;
pub use crate::shapes::hex20::*;
pub use crate::shapes::hex32::*;
pub use crate::shapes::hex8::*;
pub use crate::shapes::lin2::*;
pub use crate::shapes::lin3::*;
pub use crate::shapes::lin4::*;
pub use crate::shapes::lin5::*;
pub use crate::shapes::qua12::*;
pub use crate::shapes::qua16::*;
pub use crate::shapes::qua17::*;
pub use crate::shapes::qua4::*;
pub use crate::shapes::qua8::*;
pub use crate::shapes::qua9::*;
pub use crate::shapes::scratchpad::*;
pub use crate::shapes::shape::*;
pub use crate::shapes::state_of_shape::*;
pub use crate::shapes::tet10::*;
pub use crate::shapes::tet20::*;
pub use crate::shapes::tet4::*;
pub use crate::shapes::tri10::*;
pub use crate::shapes::tri15::*;
pub use crate::shapes::tri3::*;
pub use crate::shapes::tri6::*;
<commit_msg>Improve doc comments<commit_after>//! Interpolation functions and derivatives for geometric shapes (elements)
//!
//! **Note:** The [Shape] struct is the main component of this module whereas the Hex8,
//! Qua4, Lin2, etc. are secondary components.
//!
//! The [Shape] struct, after allocation, should be treated as immutable, although its
//! members are public (readonly). On the other hand the [StateOfShape] contains the
//! mutable data required by [Shape] to perform the computations explained below.
//!
//! # Examples
//!
//! See [Shape].
//!
//! # Definitions
//!
//! Here, we consider the following definitions:
//!
//! * `space_ndim` -- is the number of dimensions of the space under study (2 or 3)
//! * `geo_ndim` -- is the number of dimensions of the geometry element (shape).
//!                  For instance, a line in the 2D space has `geo_ndim = 1` and
//!                  `space_ndim = 2`. Another example: a triangle in the 3D space
//!                  has `geo_ndim = 2` and `space_ndim = 3`.
//! * `local` -- refers to a numbering scheme for the nodes of the shape (or element)
//! * `global` -- refers to a numbering scheme applied for the whole mesh
//! * `spatial (real) space` -- is the "real" space mapped by the x₀,x₁,x₂ coordinates (see figure below)
//! * `reference (natural) space` -- is the "virtual" space mapped by the ξ₀,ξ₁,ξ₂ coordinates (see figure below)
//!
//! **Note:** In the code, we use (r,s,t) as (ξ₀,ξ₁,ξ₂) and `ksi` to represent the vector `ξ`.
//!
//! ![Real and reference space mapping](https://github.com/cpmech/gemlab/raw/main/data/figures/mapping-real-to-reference.png)
//!
//! We also consider the following counting variables:
//!
//! * `nnode` -- (local) number of points (aka nodes) that define the shape/element.
//! * `npoint` -- (global) number of points in the whole mesh; not used in this module
//!               but important to remember
//! * `nedge` -- number of edges on the shape (2D or 3D)
//! * `nface` -- number of faces on the shape (3D only)
//! * `edge_nnode` -- number of points/nodes that define the edge
//! * `face_nnode` -- number of points/nodes that define the face
//! * `face_nedge` -- number of edges on the face
//!
//! # Isoparametric formulation
//!
//! The isoparametric formulation establishes that we can calculate the coordinates `x(ξ)`
//! within the shape/element from the shape functions `Nᵐ(ξ)` and the coordinates at each
//! node by using the formula:
//!
//! ```text
//! → →         →  →
//! x(ξ) = Σ Nᵐ(ξ) xᵐ
//!        m
//! ```
//!
//! where `x` is the (space_ndim) vector of real coordinates, `ξ` is the (geo_ndim)
//! vector of reference coordinates, `Nᵐ` are the (nnode) interpolation functions,
//! and `xᵐ` are the (nnode) coordinates of each m-node of the geometric shape.
//!
//! Given an (nnode,space_ndim) **matrix** of coordinates `X`, we can calculate the
//! (space_ndim) **vector** of coordinates `x` by means of
//!
//! ```text
//! x = Xᵀ ⋅ N
//! ```
//!
//! where `N` is an (nnode) **vector** formed with all `Nᵐ` values.
//!
//! # Derivatives on the reference space and gradients on the real space
//!
//! Here, we consider two cases:
//!
//! * General case with geo_ndim == space_ndim; and
//! * Line in multi-dimensions with geo_ndim = 1 and space_ndim > 1.
//!
//! ## General case with geo_ndim == space_ndim
//!
//! If `geo_ndim == space_ndim`, we define the Jacobian tensor as
//!
//! ```text
//!         →
//!   →    dx     →    →
//! J(ξ) = —— = Σ xᵐ ⊗ Lᵐ
//!         →   m
//!        dξ
//! ```
//!
//! where
//!
//! ```text
//!             →
//! →  →    dNᵐ(ξ)
//! Lᵐ(ξ) = ——————
//!            →
//!           dξ
//! ```
//!
//! are the derivatives of each interpolation function `Nᵐ` with respect to the
//! reference coordinate. `Lᵐ` are (geo_ndim) vectors and can be organized in
//! an (nnode,geo_ndim) matrix `L` of **local** derivatives.
//!
//! We can write the Jacobian in matrix (space_ndim,geo_ndim) notation as follows
//!
//! ```text
//! J = Xᵀ · L
//! ```
//!
//! where `X` is the (nnode,space_ndim) matrix of coordinates and `L` is the (nnode,geo_ndim) matrix.
//!
//! Next, we define the gradient of interpolation functions (i.e., derivatives of interpolation
//! functions with respect to real coordinates) by means of
//!
//! ```text
//!             →
//! →  →    dNᵐ(ξ)
//! Gᵐ(ξ) = ——————
//!            →
//!           dx
//! ```
//!
//! which can be organized in an (nnode,space_ndim) matrix `G`.
//!
//! The inverse Jacobian allows us to determine the gradient vectors `G` as follows
//!
//! ```text
//! →       →  →        →
//! Gᵐ(ξ) = Lᵐ(ξ) · J⁻¹(ξ)
//! ```
//!
//! Or, in matrix notation,
//!
//! ```text
//! G = L · J⁻¹
//! ```
//!
//! where `G` is an (nnode,space_ndim) matrix.
//!
//! ## Line in multi-dimensions (geo_ndim = 1 and space_ndim > 1)
//!
//! In this case, the Jacobian equals the (space_ndim,1) base vector `g₁` which
//! is tangent to the line element, i.e.,
//!
//! ```text
//!                          →
//! →    →     →            dx
//! J := Jline(ξ) = g₁(ξ) = —— = Xᵀ · L
//!                         dξ
//! ```
//!
//! We also consider a parametric coordinate `ℓ` which varies
//! from `0` to `ℓ_max` (the length of the line) according to
//!
//! ```text
//!                ℓ_max
//! ℓ(ξ) = (1 + ξ) —————
//!                  2
//!
//!        2 · ℓ
//! ξ(ℓ) = ————— - 1
//!        ℓ_max
//! ```
//!
//! Note that:
//!
//! ```text
//! 0 ≤ ℓ ≤ ℓ_max
//!
//! -1 ≤ ξ ≤ +1
//! ```
//!
//! # Normal vectors
//!
//! ## Line in multi-dimensions (geo_ndim = 1 and space_ndim > 1)
//!
//! Base vector tangent to the line:
//!
//! ```text
//!          →
//!         dx
//! g₁(ξ) = —— = Xᵀ · L = first_column(J)
//!         dξ
//! ```
//!
//! Normal vector:
//!
//! ```text
//! →   →    →
//! n = e₃ × g₁
//!
//!   →       →
//! ||n|| = ||g₁||
//! ```
//!
//! Thus
//!
//! ```text
//!        →           →
//! dℓ = ||g₁|| dξ = ||n|| dξ
//! ```
//!
//! For a straight line (segment):
//!
//! ```text
//!   →
//! ||n|| = Δℓ / Δξ = L / 2
//! ```
//!
//! because all [GeoClass::Lin] have `Δξ = 2`.
//!
//! ## Boundary surface (geo_ndim = 2 and space_ndim = 3)
//!
//! Base vectors tangent to the surface:
//!
//! ```text
//!          →
//! →  →    dx
//! g₁(ξ) = ——— = first_column(J)
//!         dξ₁
//!
//!          →
//! →  →    dx
//! g₂(ξ) = ——— = second_column(J)
//!         dξ₂
//! ```
//!
//! Normal vector:
//!
//! ```text
//! →   →    →
//! n = g₁ × g₂
//! ```
//!
//! Thus
//!
//! ```text
//!         →
//! dA := ||n|| dξ₁ dξ₂
//! ```
//!
//! For flat quadrilateral faces with sides perpendicular one with another
//!
//! ```text
//!   →
//! ||n|| = A / (Δξ₁ Δξ₂) = A / 4
//! ```
//!
//! because all [GeoClass::Qua] have `Δξᵢ = 2`.
//!

mod enums;
mod hex20;
mod hex32;
mod hex8;
mod lin2;
mod lin3;
mod lin4;
mod lin5;
pub mod op;
mod qua12;
mod qua16;
mod qua17;
mod qua4;
mod qua8;
mod qua9;
mod scratchpad;
mod shape;
mod state_of_shape;
mod tet10;
mod tet20;
mod tet4;
mod tri10;
mod tri15;
mod tri3;
mod tri6;
pub use crate::shapes::enums::*;
pub use crate::shapes::hex20::*;
pub use crate::shapes::hex32::*;
pub use crate::shapes::hex8::*;
pub use crate::shapes::lin2::*;
pub use crate::shapes::lin3::*;
pub use crate::shapes::lin4::*;
pub use crate::shapes::lin5::*;
pub use crate::shapes::qua12::*;
pub use crate::shapes::qua16::*;
pub use crate::shapes::qua17::*;
pub use crate::shapes::qua4::*;
pub use crate::shapes::qua8::*;
pub use crate::shapes::qua9::*;
pub use crate::shapes::scratchpad::*;
pub use crate::shapes::shape::*;
pub use crate::shapes::state_of_shape::*;
pub use crate::shapes::tet10::*;
pub use crate::shapes::tet20::*;
pub use crate::shapes::tet4::*;
pub use crate::shapes::tri10::*;
pub use crate::shapes::tri15::*;
pub use crate::shapes::tri3::*;
pub use crate::shapes::tri6::*;
<|endoftext|>