<commit_before>//! redis-rs is a rust implementation of a Redis client library.  It exposes
//! a general purpose interface to Redis and also provides specific helpers for
//! commonly used functionality.
//!
//! The crate is called `redis` and you can depend on it via cargo:
//!
//! ```ini
//! [dependencies.redis]
//! git = "https://github.com/mitsuhiko/redis-rs.git"
//! ```
//!
//! # Basic Operation
//!
//! redis-rs exposes to API levels: a low- and a high-level part.  The high-level
//! part does not expose all the functionality of redis and might take some
//! liberties in how it speaks the protocol.  The low-level part of the API
//! allows you to express any request on the redis level.  You can fluently
//! switch between both API levels at any point.
//!
//! ## Connection Handling
//!
//! For connecting to redis you can use a client object which then can produce
//! actual connections.  Connections and clients as well as results of
//! connections and clients are considered `ConnectionLike` objects and
//! can be used anywhere a request is made.
//!
//! The full canonical way to get a connection is to create a client and
//! to ask for a connection from it:
//!
//! ```rust,no_run
//! extern crate redis;
//!
//! fn main() {
//!     let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//!     let con = client.get_connection().unwrap();
//! }
//! ```
//!
//! You can however also *not* unwrap the client and connection and pass
//! the result containing a client to the functions directly.  Doing this
//! on actual clients is discouraged however as currently no connection
//! pooling takes place and you create and destroy a TCP socket constantly.
//!
//! ## Connection Parameters
//!
//! redis-rs knows different ways to define where a connection should
//! go.  The parameter to `Client::open` needs to implement the
//! `IntoConnectionInfo` trait of which there are three implementations:
//!
//! * string slices in `redis://` URL format.
//! * URL objects from the redis-url crate.
//! * `ConnectionInfo` objects.
//!
//! The URL format is `redis://[:<passwd>@]<hostname>[:port][/<db>]`
//!
//! ## Executing Low-Level Commands
//!
//! To execute low-level commands you can use the `cmd` function which allows
//! you to build redis requests.  Once you have configured a command object
//! to your liking you can send a query into any `ConnectionLike` object:
//!
//! ```rust,no_run
//! fn main() {
//!     let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//!     let con = client.get_connection();
//!     let _ : () = redis::cmd("SET").arg("my_key").arg(42i).query(&con).unwrap();
//! }
//! ```
//!
//! Upon querying the return value is a result object.  If you do not care
//! about the actual return value (other than that it is not a failure)
//! you can always type annotate it to the unit type `()`.
//!
//! ## Executing High-Level Commands
//!
//! The high-level interface is similar.  For it to become available you
//! need to use the `Commands` trait in which case all `ConnectionLike`
//! objects the library provides will also have high-level methods which
//! make working with the protocol easier:
//!
//! ```rust,no_run
//! extern crate redis;
//! use redis::Commands;
//!
//! fn main() {
//!     let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//!     let con = client.get_connection();
//!     let _ : () = con.set("my_key", 42i).unwrap();
//! }
//! ```
//!
//! Note that high-level commands are work in progress and many are still
//! missing!
//!
//! ## Type Conversions
//!
//! Because redis inherently is mostly type-less and the protocol is not
//! exactly friendly to developers, this library provides flexible support
//! for casting values to the intended results.  This is driven through the
//! `FromRedisValue` and `ToRedisArgs` traits.
//!
//! The `arg` method of the command will accept a wide range of types through
//! the `ToRedisArgs` trait and the `query` method of a command can convert the
//! value to what you expect the function to return through the `FromRedisValue`
//! trait.  This is quite flexible and allows vectors, tuples, hashsets, hashmaps
//! as well as optional values:
//!
//! ```rust,no_run
//! # use redis::Commands;
//! # use std::collections::{HashMap, HashSet};
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let count : i32 = con.get("my_counter").unwrap();
//! let count = con.get("my_counter").unwrap_or(0i32);
//! let k : Option<String> = con.get("missing_key").unwrap();
//! let name : String = con.get("my_name").unwrap();
//! let bin : Vec<u8> = con.get("my_binary").unwrap();
//! let map : HashMap<String, i32> = con.hgetall("my_hash").unwrap();
//! let keys : Vec<String> = con.hkeys("my_hash").unwrap();
//! let mems : HashSet<i32> = con.smembers("my_set").unwrap();
//! let (k1, k2) : (String, String) = con.get(["k1", "k2"].as_slice()).unwrap();
//! ```
//!
//! # Iteration Protocol
//!
//! In addition to sending a single query you iterators are also supported.  When
//! used with regular bulk responses they don't give you much over querying and
//! converting into a vector (both use a vector internally) but they can also
//! be used with `SCAN` like commands in which case iteration will send more
//! queries until the cursor is exhausted:
//!
//! ```rust,no_run
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let mut iter : redis::Iter<int> = redis::cmd("SSCAN").arg("my_set")
//!     .cursor_arg(0).iter(&con).unwrap();
//! for x in iter {
//!     // do something with the item
//! }
//! ```
//!
//! As you can see the cursor argument needs to be defined with `cursor_arg`
//! instead of `arg` so that the library knows which argument needs updating
//! as the query is run for more items.
//!
//! # Pipelining
//!
//! In addition to simple queries you can also send command pipelines.  This
//! is provided through the `pipe` function.  It works very similar to sending
//! individual commands but you can send more than one in one go.  This also
//! allows you to ignore individual results so that matching on the end result
//! is easier:
//!
//! ```rust,no_run
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let (k1, k2) : (i32, i32) = redis::pipe()
//!     .cmd("SET").arg("key_1").arg(42i).ignore()
//!     .cmd("SET").arg("key_2").arg(43i).ignore()
//!     .cmd("GET").arg("key_1")
//!     .cmd("GET").arg("key_2").query(&con).unwrap();
//! ```
//!
//! If you want the pipeline to be wrapped in a `MULTI`/`EXEC` block you can
//! easily do that by switching the pipeline into `atomic` mode.  From the
//! caller's point of view nothing changes, the pipeline itself will take
//! care of the rest for you:
//!
//! ```rust,no_run
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let (k1, k2) : (i32, i32) = redis::pipe()
//!     .atomic()
//!     .cmd("SET").arg("key_1").arg(42i).ignore()
//!     .cmd("SET").arg("key_2").arg(43i).ignore()
//!     .cmd("GET").arg("key_1")
//!     .cmd("GET").arg("key_2").query(&con).unwrap();
//! ```
//!
//! You can also use high-level commands on pipelines through the
//! `PipelineCommands` trait:
//!
//! ```rust,no_run
//! use redis::PipelineCommands;
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let (k1, k2) : (i32, i32) = redis::pipe()
//!     .atomic()
//!     .set("key_1", 42i).ignore()
//!     .set("key_2", 43i).ignore()
//!     .get("key_1")
//!     .get("key_2").query(&con).unwrap();
//! ```
//!
//! # Transactions
//!
//! Transactions are available through atomic pipelines.  In order to use
//! them in a more simple way you can use the `transaction` function of a
//! connection:
//!
//! ```rust,no_run
//! use redis::{Commands, PipelineCommands};
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let key = "the_key";
//! let (new_val,) : (int,) = redis::transaction(&con, [key].as_slice(), |pipe| {
//!     let old_val : int = try!(con.get(key));
//!     pipe
//!         .set(key, old_val + 1).ignore()
//!         .get(key).query(&con)
//! }).unwrap();
//! println!("The incremented number is: {}", new_val);
//! ```
//!
//! For more information see the `transaction` function.
//!
//! # PubSub
//!
//! Pubsub is currently work in progress but provided through the `PubSub`
//! connection object.  Due to the fact that Rust does not have support
//! for async IO in libnative yet, the API does not provide a way to
//! read messages with any form of timeout yet.
//!
//! Example usage:
//!
//! ```rust,no_run
//! let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! let mut pubsub = client.get_pubsub().unwrap();
//! pubsub.subscribe("channel_1").unwrap();
//! pubsub.subscribe("channel_2").unwrap();
//!
//! loop {
//!     let msg = pubsub.get_message().unwrap();
//!     let payload : String = msg.get_payload().unwrap();
//!     println!("channel '{}': {}", msg.get_channel_name(), payload);
//! }
//! ```
//!
//! # Scripts
//!
//! Lua scripts are supported through the `Script` type in a convenient
//! way (it does not support pipelining currently).  It will automatically
//! load the script if it does not exist and invoke it.
//!
//! Example:
//!
//! ```rust,no_run
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let script = redis::Script::new(r"
//!     return tonumber(ARGV[1]) + tonumber(ARGV[2]);
//! ");
//! let result : int = script.arg(1i).arg(2i).invoke(&con).unwrap();
//! assert_eq!(result, 3);
//! ```

#![crate_name = "redis"]
#![crate_type = "lib"]
#![license = "BSD"]
#![comment = "Bindings and wrapper functions for redis."]

#![deny(non_camel_case_types)]

#![feature(macro_rules)]
#![feature(default_type_params)]
#![feature(slicing_syntax)]

#![experimental]

extern crate url;
extern crate serialize;
extern crate "rust-crypto" as crypto;

/* public api */
pub use parser::{parse_redis_value, Parser};
pub use client::Client;
pub use script::{Script, ScriptInvocation};
pub use connection::{Connection, ConnectionLike, ConnectionInfo,
                     IntoConnectionInfo, PubSub, Msg, transaction,
                     parse_redis_url};
pub use cmd::{cmd, Cmd, pipe, Pipeline, Iter, pack_command};
pub use commands::{
    Commands,
    PipelineCommands,
};

#[doc(hidden)]
pub use types::{
    /* low level values */
    Value,
        Nil,
        Int,
        Data,
        Bulk,
        Okay,
        Status,

    /* error and result types */
    RedisError,
    RedisResult,

    /* error kinds */
    ErrorKind,
        ResponseError,
        TypeError,
        ExecAbortError,
        BusyLoadingError,
        NoScriptError,
        ExtensionError,
        InternalIoError,

    /* utility types */
    InfoDict,
    NumericBehavior,
        NonNumeric,
        NumberIsInteger,
        NumberIsFloat,

    /* conversion traits */
    FromRedisValue,
    ToRedisArgs,

    /* utility functions */
    from_redis_value,
};

mod macros;

mod parser;
mod client;
mod connection;
mod types;
mod script;
mod cmd;
mod commands;
<commit_msg>Removed unwrap from examples<commit_after>//! redis-rs is a rust implementation of a Redis client library.  It exposes
//! a general purpose interface to Redis and also provides specific helpers for
//! commonly used functionality.
//!
//! The crate is called `redis` and you can depend on it via cargo:
//!
//! ```ini
//! [dependencies.redis]
//! git = "https://github.com/mitsuhiko/redis-rs.git"
//! ```
//!
//! # Basic Operation
//!
//! redis-rs exposes to API levels: a low- and a high-level part.  The high-level
//! part does not expose all the functionality of redis and might take some
//! liberties in how it speaks the protocol.  The low-level part of the API
//! allows you to express any request on the redis level.  You can fluently
//! switch between both API levels at any point.
//!
//! ## Connection Handling
//!
//! For connecting to redis you can use a client object which then can produce
//! actual connections.  Connections and clients as well as results of
//! connections and clients are considered `ConnectionLike` objects and
//! can be used anywhere a request is made.
//!
//! The full canonical way to get a connection is to create a client and
//! to ask for a connection from it:
//!
//! ```rust,no_run
//! extern crate redis;
//!
//! fn do_something() -> redis::RedisResult<()> {
//!     let client = try!(redis::Client::open("redis://127.0.0.1/"));
//!     let con = try!(client.get_connection());
//!
//!     /* do something here */
//!
//!     Ok(())
//! }
//! # fn main() {}
//! ```
//!
//! ## Connection Parameters
//!
//! redis-rs knows different ways to define where a connection should
//! go.  The parameter to `Client::open` needs to implement the
//! `IntoConnectionInfo` trait of which there are three implementations:
//!
//! * string slices in `redis://` URL format.
//! * URL objects from the redis-url crate.
//! * `ConnectionInfo` objects.
//!
//! The URL format is `redis://[:<passwd>@]<hostname>[:port][/<db>]`
//!
//! ## Executing Low-Level Commands
//!
//! To execute low-level commands you can use the `cmd` function which allows
//! you to build redis requests.  Once you have configured a command object
//! to your liking you can send a query into any `ConnectionLike` object:
//!
//! ```rust,no_run
//! fn do_something(con: &redis::Connection) -> redis::RedisResult<()> {
//!     let _ : () = try!(redis::cmd("SET").arg("my_key").arg(42i).query(con));
//!     Ok(())
//! }
//! ```
//!
//! Upon querying the return value is a result object.  If you do not care
//! about the actual return value (other than that it is not a failure)
//! you can always type annotate it to the unit type `()`.
//!
//! ## Executing High-Level Commands
//!
//! The high-level interface is similar.  For it to become available you
//! need to use the `Commands` trait in which case all `ConnectionLike`
//! objects the library provides will also have high-level methods which
//! make working with the protocol easier:
//!
//! ```rust,no_run
//! extern crate redis;
//! use redis::Commands;
//!
//! fn do_something(con: &redis::Connection) -> redis::RedisResult<()> {
//!     let _ : () = try!(con.set("my_key", 42i));
//!     Ok(())
//! }
//! # fn main() {}
//! ```
//!
//! Note that high-level commands are work in progress and many are still
//! missing!
//!
//! ## Type Conversions
//!
//! Because redis inherently is mostly type-less and the protocol is not
//! exactly friendly to developers, this library provides flexible support
//! for casting values to the intended results.  This is driven through the
//! `FromRedisValue` and `ToRedisArgs` traits.
//!
//! The `arg` method of the command will accept a wide range of types through
//! the `ToRedisArgs` trait and the `query` method of a command can convert the
//! value to what you expect the function to return through the `FromRedisValue`
//! trait.  This is quite flexible and allows vectors, tuples, hashsets, hashmaps
//! as well as optional values:
//!
//! ```rust,no_run
//! # use redis::Commands;
//! # use std::collections::{HashMap, HashSet};
//! # fn do_something() -> redis::RedisResult<()> {
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let count : i32 = try!(con.get("my_counter"));
//! let count = con.get("my_counter").unwrap_or(0i32);
//! let k : Option<String> = try!(con.get("missing_key"));
//! let name : String = try!(con.get("my_name"));
//! let bin : Vec<u8> = try!(con.get("my_binary"));
//! let map : HashMap<String, i32> = try!(con.hgetall("my_hash"));
//! let keys : Vec<String> = try!(con.hkeys("my_hash"));
//! let mems : HashSet<i32> = try!(con.smembers("my_set"));
//! let (k1, k2) : (String, String) = try!(con.get(["k1", "k2"].as_slice()));
//! # Ok(())
//! # }
//! ```
//!
//! # Iteration Protocol
//!
//! In addition to sending a single query you iterators are also supported.  When
//! used with regular bulk responses they don't give you much over querying and
//! converting into a vector (both use a vector internally) but they can also
//! be used with `SCAN` like commands in which case iteration will send more
//! queries until the cursor is exhausted:
//!
//! ```rust,no_run
//! # fn do_something() -> redis::RedisResult<()> {
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let mut iter : redis::Iter<int> = try!(redis::cmd("SSCAN").arg("my_set")
//!     .cursor_arg(0).iter(&con));
//! for x in iter {
//!     // do something with the item
//! }
//! # Ok(()) }
//! ```
//!
//! As you can see the cursor argument needs to be defined with `cursor_arg`
//! instead of `arg` so that the library knows which argument needs updating
//! as the query is run for more items.
//!
//! # Pipelining
//!
//! In addition to simple queries you can also send command pipelines.  This
//! is provided through the `pipe` function.  It works very similar to sending
//! individual commands but you can send more than one in one go.  This also
//! allows you to ignore individual results so that matching on the end result
//! is easier:
//!
//! ```rust,no_run
//! # fn do_something() -> redis::RedisResult<()> {
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let (k1, k2) : (i32, i32) = try!(redis::pipe()
//!     .cmd("SET").arg("key_1").arg(42i).ignore()
//!     .cmd("SET").arg("key_2").arg(43i).ignore()
//!     .cmd("GET").arg("key_1")
//!     .cmd("GET").arg("key_2").query(&con));
//! # Ok(()) }
//! ```
//!
//! If you want the pipeline to be wrapped in a `MULTI`/`EXEC` block you can
//! easily do that by switching the pipeline into `atomic` mode.  From the
//! caller's point of view nothing changes, the pipeline itself will take
//! care of the rest for you:
//!
//! ```rust,no_run
//! # fn do_something() -> redis::RedisResult<()> {
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let (k1, k2) : (i32, i32) = try!(redis::pipe()
//!     .atomic()
//!     .cmd("SET").arg("key_1").arg(42i).ignore()
//!     .cmd("SET").arg("key_2").arg(43i).ignore()
//!     .cmd("GET").arg("key_1")
//!     .cmd("GET").arg("key_2").query(&con));
//! # Ok(()) }
//! ```
//!
//! You can also use high-level commands on pipelines through the
//! `PipelineCommands` trait:
//!
//! ```rust,no_run
//! # fn do_something() -> redis::RedisResult<()> {
//! use redis::PipelineCommands;
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let (k1, k2) : (i32, i32) = try!(redis::pipe()
//!     .atomic()
//!     .set("key_1", 42i).ignore()
//!     .set("key_2", 43i).ignore()
//!     .get("key_1")
//!     .get("key_2").query(&con));
//! # Ok(()) }
//! ```
//!
//! # Transactions
//!
//! Transactions are available through atomic pipelines.  In order to use
//! them in a more simple way you can use the `transaction` function of a
//! connection:
//!
//! ```rust,no_run
//! # fn do_something() -> redis::RedisResult<()> {
//! use redis::{Commands, PipelineCommands};
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let key = "the_key";
//! let (new_val,) : (int,) = try!(redis::transaction(&con, [key].as_slice(), |pipe| {
//!     let old_val : int = try!(con.get(key));
//!     pipe
//!         .set(key, old_val + 1).ignore()
//!         .get(key).query(&con)
//! }));
//! println!("The incremented number is: {}", new_val);
//! # Ok(()) }
//! ```
//!
//! For more information see the `transaction` function.
//!
//! # PubSub
//!
//! Pubsub is currently work in progress but provided through the `PubSub`
//! connection object.  Due to the fact that Rust does not have support
//! for async IO in libnative yet, the API does not provide a way to
//! read messages with any form of timeout yet.
//!
//! Example usage:
//!
//! ```rust,no_run
//! # fn do_something() -> redis::RedisResult<()> {
//! let client = try!(redis::Client::open("redis://127.0.0.1/"));
//! let mut pubsub = try!(client.get_pubsub());
//! try!(pubsub.subscribe("channel_1"));
//! try!(pubsub.subscribe("channel_2"));
//!
//! loop {
//!     let msg = try!(pubsub.get_message());
//!     let payload : String = try!(msg.get_payload());
//!     println!("channel '{}': {}", msg.get_channel_name(), payload);
//! }
//! # }
//! ```
//!
//! # Scripts
//!
//! Lua scripts are supported through the `Script` type in a convenient
//! way (it does not support pipelining currently).  It will automatically
//! load the script if it does not exist and invoke it.
//!
//! Example:
//!
//! ```rust,no_run
//! # fn do_something() -> redis::RedisResult<()> {
//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! # let con = client.get_connection().unwrap();
//! let script = redis::Script::new(r"
//!     return tonumber(ARGV[1]) + tonumber(ARGV[2]);
//! ");
//! let result : int = try!(script.arg(1i).arg(2i).invoke(&con));
//! assert_eq!(result, 3);
//! # Ok(()) }
//! ```

#![crate_name = "redis"]
#![crate_type = "lib"]
#![license = "BSD"]
#![comment = "Bindings and wrapper functions for redis."]

#![deny(non_camel_case_types)]

#![feature(macro_rules)]
#![feature(default_type_params)]
#![feature(slicing_syntax)]

#![experimental]

extern crate url;
extern crate serialize;
extern crate "rust-crypto" as crypto;

/* public api */
pub use parser::{parse_redis_value, Parser};
pub use client::Client;
pub use script::{Script, ScriptInvocation};
pub use connection::{Connection, ConnectionLike, ConnectionInfo,
                     IntoConnectionInfo, PubSub, Msg, transaction,
                     parse_redis_url};
pub use cmd::{cmd, Cmd, pipe, Pipeline, Iter, pack_command};
pub use commands::{
    Commands,
    PipelineCommands,
};

#[doc(hidden)]
pub use types::{
    /* low level values */
    Value,
        Nil,
        Int,
        Data,
        Bulk,
        Okay,
        Status,

    /* error and result types */
    RedisError,
    RedisResult,

    /* error kinds */
    ErrorKind,
        ResponseError,
        TypeError,
        ExecAbortError,
        BusyLoadingError,
        NoScriptError,
        ExtensionError,
        InternalIoError,

    /* utility types */
    InfoDict,
    NumericBehavior,
        NonNumeric,
        NumberIsInteger,
        NumberIsFloat,

    /* conversion traits */
    FromRedisValue,
    ToRedisArgs,

    /* utility functions */
    from_redis_value,
};

mod macros;

mod parser;
mod client;
mod connection;
mod types;
mod script;
mod cmd;
mod commands;
<|endoftext|>


<commit_before>//! Length (base unit meter, m1).

quantity! {
    /// Length (base unit meter, m1).
    quantity: Length; "length";
    /// Length dimension, m1.
    dimension: ISQ<
        P1,     // length
        Z0,     // mass
        Z0,     // time
        Z0,     // electric current
        Z0,     // thermodynamic temperature
        Z0,     // amount of substance
        Z0>;    // luminous intensity
    units {
        @yottameter: prefix!(yotta); "Ym", "yottameter", "yottameters";
        @zettameter: prefix!(zetta); "Zm", "zettameter", "zettameters";
        @exameter: prefix!(exa); "Em", "exameter", "exameters";
        @petameter: prefix!(peta); "Pm", "petameter", "petameters";
        @terameter: prefix!(tera); "Tm", "terameter", "terameters";
        @gigameter: prefix!(giga); "Gm", "gigameter", "gigameters";
        @megameter: prefix!(mega); "Mm", "megameter", "megameters";
        @kilometer: prefix!(kilo); "km", "kilometer", "kilometers";
        @hectometer: prefix!(hecto); "hm", "hectometer", "hectometers";
        @decameter: prefix!(deca); "dam", "decameter", "decameters";
        /// The meter is the length of the path travelled by light in a vacuum during a time
        /// interval of 1/299 792 458 of a second.
        @meter: prefix!(none); "m", "meter", "meters";
        @decimeter: prefix!(deci); "dm", "decimeter", "decimeters";
        @centimeter: prefix!(centi); "cm", "centimeter", "centimeters";
        @millimeter: prefix!(milli); "mm", "millimeter", "millimeters";
        @micrometer: prefix!(micro); "µm", "micrometer", "micrometers";
        @nanometer: prefix!(nano); "nm", "nanometer", "nanometers";
        @picometer: prefix!(pico); "pm", "picometer", "picometers";
        @femtometer: prefix!(femto); "fm", "femtometer", "femtometers";
        @attometer: prefix!(atto); "am", "attometer", "attometers";
        @zeptometer: prefix!(zepto); "zm", "zeptometer", "zeptometers";
        @yoctometer: prefix!(yocto); "ym", "yoctometer", "yoctometers";

        @angstrom: 1.0_E-10; "Å", "ångström", "ångströms";
        @astronomical_unit: 1.495_979_E11; "ua", "astronomical unit", "astronomical units";
        @chain: 2.011_684_E1; "ch", "chain", "chains";
        @fathom: 1.828_804_E0; "fathom", "fathom", "fathoms";
        @fermi: 1.0_E-15; "fm", "fermi", "fermis";
        @foot: 3.048_E-1; "ft", "foot", "feet";
        @foot_survey: 3.048_006_E-1; "ft", "foot (U.S. survey)", "feet (U.S. survey)";
        @inch: 2.54_E-2; "in", "inch", "inches";
        @light_year: 9.460_73_E15; "l. y.", "light year", "light years";
        @microinch: 2.54_E-8; "μin", "microinch", "microinches";
        @micron: 1.0_E-6; "μ", "micron", "microns";
        @mil: 2.54_E-5; "0.001 in", "mil", "mils";
        @mile: 1.609_344_E3; "mi", "mile", "miles";
        @mile_survey: 1.609_347_E3; "mi", "mile (U.S. survey)", "miles (U.S. survey)";
        @nautical_mile: 1.852_E3; "M", "nautical mile", "nautical miles";
        @parsec: 3.085_678_E16; "pc", "parsec", "parsecs";
        @pica_computer: 4.233_333_333_333_333_E-3; "1/6 in", "pica (computer)", "picas (computer)";
        @pica_printers: 4.217_518_E-3; "1/6 in", "pica (printer's)", "picas (printer's)";
        @point_computer: 3.527_778_E-4; "1/72 in", "point (computer)", "points (computer)";
        @point_printers: 3.514_598_E-4; "1/72 in", "point (printer's)", "points (printer's)";
        @rod: 5.029_21_E0; "rd", "rod", "rods";
        @yard: 9.144E-1; "yd", "yard", "yards";
    }
    impl {
        /// Calculates the length of the hypotenuse of a right-angle triangle given the legs.
        #[cfg(feature = "std")]
        #[inline(always)]
        pub fn hypot(self, other: Self) -> Self {
            Length {
                dimension: $crate::stdlib::marker::PhantomData,
                units: $crate::stdlib::marker::PhantomData,
                value: self.value.hypot(other.value)
            }
        }
    }
}

#[cfg(test)]
macro_rules! test {
    ($V:ident) => {
        use ::si::$V::*;
        use ::si::length::meter;

        quickcheck! {
            #[cfg(feature = "std")]
            #[allow(trivial_casts)]
            fn hypot(l: $V, r: $V) -> bool {
                l.hypot(r) == Length::new::<meter>(l).hypot(Length::new::<meter>(r)).get(meter)
            }
        }
    };
}

#[cfg(test)]
mod tests {
    #[cfg(feature = "f32")]
    mod f32 {
        test!(f32);
    }

    #[cfg(feature = "f64")]
    mod f64 {
        test!(f64);
    }
}<commit_msg>Fix typo in length.rs.<commit_after>//! Length (base unit meter, m1).

quantity! {
    /// Length (base unit meter, m1).
    quantity: Length; "length";
    /// Length dimension, m1.
    dimension: ISQ<
        P1,     // length
        Z0,     // mass
        Z0,     // time
        Z0,     // electric current
        Z0,     // thermodynamic temperature
        Z0,     // amount of substance
        Z0>;    // luminous intensity
    units {
        @yottameter: prefix!(yotta); "Ym", "yottameter", "yottameters";
        @zettameter: prefix!(zetta); "Zm", "zettameter", "zettameters";
        @exameter: prefix!(exa); "Em", "exameter", "exameters";
        @petameter: prefix!(peta); "Pm", "petameter", "petameters";
        @terameter: prefix!(tera); "Tm", "terameter", "terameters";
        @gigameter: prefix!(giga); "Gm", "gigameter", "gigameters";
        @megameter: prefix!(mega); "Mm", "megameter", "megameters";
        @kilometer: prefix!(kilo); "km", "kilometer", "kilometers";
        @hectometer: prefix!(hecto); "hm", "hectometer", "hectometers";
        @decameter: prefix!(deca); "dam", "decameter", "decameters";
        /// The meter is the length of the path travelled by light in a vacuum during a time
        /// interval of 1/299 792 458 of a second.
        @meter: prefix!(none); "m", "meter", "meters";
        @decimeter: prefix!(deci); "dm", "decimeter", "decimeters";
        @centimeter: prefix!(centi); "cm", "centimeter", "centimeters";
        @millimeter: prefix!(milli); "mm", "millimeter", "millimeters";
        @micrometer: prefix!(micro); "µm", "micrometer", "micrometers";
        @nanometer: prefix!(nano); "nm", "nanometer", "nanometers";
        @picometer: prefix!(pico); "pm", "picometer", "picometers";
        @femtometer: prefix!(femto); "fm", "femtometer", "femtometers";
        @attometer: prefix!(atto); "am", "attometer", "attometers";
        @zeptometer: prefix!(zepto); "zm", "zeptometer", "zeptometers";
        @yoctometer: prefix!(yocto); "ym", "yoctometer", "yoctometers";

        @angstrom: 1.0_E-10; "Å", "ångström", "ångströms";
        @astronomical_unit: 1.495_979_E11; "ua", "astronomical unit", "astronomical units";
        @chain: 2.011_684_E1; "ch", "chain", "chains";
        @fathom: 1.828_804_E0; "fathom", "fathom", "fathoms";
        @fermi: 1.0_E-15; "fm", "fermi", "fermis";
        @foot: 3.048_E-1; "ft", "foot", "feet";
        @foot_survey: 3.048_006_E-1; "ft", "foot (U.S. survey)", "feet (U.S. survey)";
        @inch: 2.54_E-2; "in", "inch", "inches";
        @light_year: 9.460_73_E15; "l. y.", "light year", "light years";
        @microinch: 2.54_E-8; "μin", "microinch", "microinches";
        @micron: 1.0_E-6; "μ", "micron", "microns";
        @mil: 2.54_E-5; "0.001 in", "mil", "mils";
        @mile: 1.609_344_E3; "mi", "mile", "miles";
        @mile_survey: 1.609_347_E3; "mi", "mile (U.S. survey)", "miles (U.S. survey)";
        @nautical_mile: 1.852_E3; "M", "nautical mile", "nautical miles";
        @parsec: 3.085_678_E16; "pc", "parsec", "parsecs";
        @pica_computer: 4.233_333_333_333_333_E-3; "1/6 in", "pica (computer)", "picas (computer)";
        @pica_printers: 4.217_518_E-3; "1/6 in", "pica (printer's)", "picas (printer's)";
        @point_computer: 3.527_778_E-4; "1/72 in", "point (computer)", "points (computer)";
        @point_printers: 3.514_598_E-4; "1/72 in", "point (printer's)", "points (printer's)";
        @rod: 5.029_21_E0; "rd", "rod", "rods";
        @yard: 9.144_E-1; "yd", "yard", "yards";
    }
    impl {
        /// Calculates the length of the hypotenuse of a right-angle triangle given the legs.
        #[cfg(feature = "std")]
        #[inline(always)]
        pub fn hypot(self, other: Self) -> Self {
            Length {
                dimension: $crate::stdlib::marker::PhantomData,
                units: $crate::stdlib::marker::PhantomData,
                value: self.value.hypot(other.value)
            }
        }
    }
}

#[cfg(test)]
macro_rules! test {
    ($V:ident) => {
        use ::si::$V::*;
        use ::si::length::meter;

        quickcheck! {
            #[cfg(feature = "std")]
            #[allow(trivial_casts)]
            fn hypot(l: $V, r: $V) -> bool {
                l.hypot(r) == Length::new::<meter>(l).hypot(Length::new::<meter>(r)).get(meter)
            }
        }
    };
}

#[cfg(test)]
mod tests {
    #[cfg(feature = "f32")]
    mod f32 {
        test!(f32);
    }

    #[cfg(feature = "f64")]
    mod f64 {
        test!(f64);
    }
}
<|endoftext|>


<commit_before>use websocket::client::Url;
use super::{Specifier,Result};
use std::rc::Rc;

pub fn ws_l_prefix(s:&str) -> Option<&str> {
    if    s.starts_with("ws-l:")
       || s.starts_with("l-ws:")
    {
        Some(&s[5..])
    }
    else if  s.starts_with("ws-listen:")
          || s.starts_with("listen-ws:")
    {
        Some(&s[10..])
    } else {
        None
    }
}

pub fn ws_c_prefix(s:&str) -> Option<&str> {
    if    s.starts_with("ws-c:")
       || s.starts_with("c-ws:")
    {
        Some(&s[5..])
    }
    else if  s.starts_with("ws-connect:")
          || s.starts_with("connect-ws:")
    {
        Some(&s[11..])
    } else {
        None
    }
}

pub fn reuser_prefix(s:&str) -> Option<&str> {
    if s.starts_with("reuse:") {
        Some(&s[6..])
    } else {
        None
    }
}

pub fn ws_url_prefix(s:&str) -> Option<&str> {
    if s.starts_with("ws://") {
        Some(s)
    } else
    if s.starts_with("wss://") {
        Some(s)
    } else {
        None
    }
}

pub fn unix_specific_prefix(s:&str) -> Option<&str> {
    if s.starts_with("unix") {
        Some(s)
    } else
    if s.starts_with("abstract") {
        #[cfg(not(any(target_os = "linux", target_os = "android")))]
        {
            warn!("Abstract-namespaced UNIX sockets are unlikely to be supported here");
        }
        Some(s)
    } else {
        None
    }
}

pub fn boxup<T:Specifier+'static>(x:T) -> Result<Rc<Specifier>> {
    Ok(Rc::new(x))
}

pub fn spec(s : &str) -> Result<Rc<Specifier>>  {
    Specifier::from_str(s)
}

impl Specifier {
    fn from_str(s: &str) -> Result<Rc<Specifier>> {
        if s == "-" || s == "inetd:" || s == "stdio:" {
            #[cfg(all(unix,not(feature="no_unix_stdio")))]
            {
                boxup(super::stdio_peer::Stdio)
            }
            #[cfg(any(not(unix),feature="no_unix_stdio"))]
            {
                boxup(super::stdio_threaded_peer::ThreadedStdio)
            }
        } else
        if s == "threadedstdio:" {
            boxup(super::stdio_threaded_peer::ThreadedStdio)
        } else
        if s == "mirror:" {
            boxup(super::mirror_peer::Mirror)
        } else
        if s == "clogged:" {
            boxup(super::trivial_peer::Clogged)
        } else
        if s.starts_with("literal:"){
            boxup(super::trivial_peer::Literal(s[8..].as_bytes().to_vec()))
        } else
        if s.starts_with("literalreply:"){
            boxup(super::mirror_peer::LiteralReply(s[13..].as_bytes().to_vec()))
        } else
        if s.starts_with("assert:"){
            boxup(super::trivial_peer::Assert(s[7..].as_bytes().to_vec()))
        } else
        if s.starts_with("tcp:") {
            boxup(super::net_peer::TcpConnect(s[4..].parse()?))
        } else
        if s.starts_with("tcp-connect:") {
            boxup(super::net_peer::TcpConnect(s[12..].parse()?))
        } else
        if s.starts_with("connect-tcp:") {
            boxup(super::net_peer::TcpConnect(s[12..].parse()?))
        } else
        if s.starts_with("c-tcp:") {
            boxup(super::net_peer::TcpConnect(s[6..].parse()?))
        } else
        if s.starts_with("tcp-c:") {
            boxup(super::net_peer::TcpConnect(s[6..].parse()?))
        } else
        if s.starts_with("tcp-l:") {
            boxup(super::net_peer::TcpListen(s[6..].parse()?))
        } else
        if s.starts_with("l-tcp:") {
            boxup(super::net_peer::TcpListen(s[6..].parse()?))
        } else
        if s.starts_with("tcp-listen:") {
            boxup(super::net_peer::TcpListen(s[11..].parse()?))
        } else
        if s.starts_with("listen-tcp:") {
            boxup(super::net_peer::TcpListen(s[11..].parse()?))
        } else
        if s.starts_with("udp:") {
            boxup(super::net_peer::UdpConnect(s[4..].parse()?))
        } else
        if s.starts_with("udp-connect:") {
            boxup(super::net_peer::UdpConnect(s[12..].parse()?))
        } else
        if s.starts_with("connect-udp:") {
            boxup(super::net_peer::UdpConnect(s[12..].parse()?))
        } else
        if s.starts_with("udp-c:") {
            boxup(super::net_peer::UdpConnect(s[6..].parse()?))
        } else
        if s.starts_with("c-udp:") {
            boxup(super::net_peer::UdpConnect(s[6..].parse()?))
        } else
        if s.starts_with("udp-listen:") {
            boxup(super::net_peer::UdpListen(s[11..].parse()?))
        } else
        if s.starts_with("listen-udp:") {
            boxup(super::net_peer::UdpListen(s[11..].parse()?))
        } else
        if s.starts_with("udp-l:") {
            boxup(super::net_peer::UdpListen(s[6..].parse()?))
        } else
        if s.starts_with("l-udp:") {
            boxup(super::net_peer::UdpListen(s[6..].parse()?))
        } else
        if let Some(x) = ws_l_prefix(s) {
            if x == "" {
                Err("Specify underlying protocol for ws-l:")?;
            }
            if let Some(c) = x.chars().next() {
                if c.is_numeric() || c == '[' {
                    // Assuming user uses old format like ws-l:127.0.0.1:8080
                    return spec(&("ws-l:tcp-l:".to_owned() + x));
                }
            }
            boxup(super::ws_server_peer::WsUpgrade(spec(x)?))
        } else
        if let Some(x) = ws_c_prefix(s) {
            // TODO: de-hardcode URI
            boxup(super::ws_client_peer::WsConnect(spec(x)?))
        } else
        if let Some(x) = reuser_prefix(s) {
            boxup(super::connection_reuse_peer::Reuser(spec(x)?))
        } else
        if let Some(url_s) = ws_url_prefix(s) {
            let url : Url = url_s.parse()?;
            boxup(super::ws_client_peer::WsClient(url))
        } else
        if s.starts_with("autoreconnect:") {
            boxup(super::reconnect_peer::AutoReconnect(spec(&s[14..])?))
        } else
        if s.starts_with("open:") {
            return Err("There is no `open:` specifier. Consider `open-async:` or `readfile:` or `writefile:`")?;
        } else
        if s.starts_with("open-async:") {
            #[cfg(all(unix,not(feature="no_unix_stdio")))]
            {
                boxup(super::stdio_peer::OpenAsync(s[11..].into()))
            }
            #[cfg(any(not(unix),feature="no_unix_stdio"))]
            {
                Err("`open-async:` is not supported in this Websocat build")?
            }
        } else
        if s.starts_with("open-fd:") {
            #[cfg(all(unix,not(feature="no_unix_stdio")))]
            {
                boxup(super::stdio_peer::OpenFdAsync(s[8..].parse()?))
            }
            #[cfg(any(not(unix),feature="no_unix_stdio"))]
            {
                Err("`open-fd:` is not supported in this Websocat build")?
            }
        } else
        if s.starts_with("readfile:") {
            boxup(super::file_peer::ReadFile(s[9..].into()))
        } else
        if s.starts_with("writefile:") {
            boxup(super::file_peer::WriteFile(s[10..].into()))
        } else
        if let Some(ss) = unix_specific_prefix(s) {
            #[cfg(unix)]
            {
                if ss.starts_with("unix:") {
                    boxup(super::unix_peer::UnixConnect(s[5..].into()))
                } else
                if ss.starts_with("unix-connect:") {
                    boxup(super::unix_peer::UnixConnect(s[13..].into()))
                } else
                if ss.starts_with("connect-unix:") {
                    boxup(super::unix_peer::UnixConnect(s[13..].into()))
                } else
                if ss.starts_with("unix-c:") {
                    boxup(super::unix_peer::UnixConnect(s[7..].into()))
                } else
                if ss.starts_with("c-unix:") {
                    boxup(super::unix_peer::UnixConnect(s[7..].into()))
                } else
                if ss.starts_with("unix-listen:") {
                    boxup(super::unix_peer::UnixListen(s[12..].into()))
                } else
                if ss.starts_with("unix-l:") {
                    boxup(super::unix_peer::UnixListen(s[7..].into()))
                } else
                if ss.starts_with("listen-unix:") {
                    boxup(super::unix_peer::UnixListen(s[12..].into()))
                } else
                if ss.starts_with("l-unix:") {
                    boxup(super::unix_peer::UnixListen(s[7..].into()))
                } else
                if ss.starts_with("unix-dgram:") || ss.starts_with("dgram-unix:") {
                    let splits : Vec<&str> = s[11..].split(":").collect();
                    if splits.len() != 2 {
                        Err("Expected two colon-separted paths")?;
                    }
                    boxup(super::unix_peer::UnixDgram(splits[0].into(),splits[1].into()))
                } else
                if ss.starts_with("abstract:") {
                    boxup(super::unix_peer::AbstractConnect(s[9..].into()))
                } else
                if ss.starts_with("abstract-connect:") {
                    boxup(super::unix_peer::AbstractConnect(s[17..].into()))
                } else
                if ss.starts_with("connect-abstract:") {
                    boxup(super::unix_peer::AbstractConnect(s[17..].into()))
                } else
                if ss.starts_with("abstract-c:") {
                    boxup(super::unix_peer::AbstractConnect(s[11..].into()))
                } else
                if ss.starts_with("c-abstract:") {
                    boxup(super::unix_peer::AbstractConnect(s[11..].into()))
                } else
                if ss.starts_with("abstract-listen:") {
                    boxup(super::unix_peer::AbstractListen(s[16..].into()))
                } else
                if ss.starts_with("abstract-l:") {
                    boxup(super::unix_peer::AbstractListen(s[11..].into()))
                } else
                if ss.starts_with("listen-abstract:") {
                    boxup(super::unix_peer::AbstractListen(s[16..].into()))
                } else
                if ss.starts_with("l-abstract:") {
                    boxup(super::unix_peer::AbstractListen(s[11..].into()))
                } else
                if ss.starts_with("abstract-dgram:") || ss.starts_with("dgram-abstract:") {
                    let splits : Vec<&str> = s[15..].split(":").collect();
                    if splits.len() != 2 {
                        Err("Expected two colon-separted addresses")?;
                    }
                    boxup(super::unix_peer::AbstractDgram(splits[0].into(),splits[1].into()))
                } else
                {
                    error!("Invalid specifier string `{}`", ss);
                    Err("Wrong specifier")?
                }
            }
            #[cfg(not(unix))]
            {
                let _ss = ss;
                Err("`unix*:` or `abstract*:` are not supported in this Websocat build")?
            }
        } else
        if s == "inetd-ws:" {
            spec("ws-l:inetd:")
        } else
        if s.starts_with("l-ws-unix:") {
            spec(&format!("ws-l:unix-l:{}", &s[10..]))
        } else
        if s.starts_with("l-ws-abstract:") {
            spec(&format!("ws-l:abstract-l::{}", &s[14..]))
        } else
        if s.starts_with("sh-c:") {
            #[cfg(feature="tokio-process")]
            { boxup(super::process_peer::ShC(s[5..].into())) }
            #[cfg(not(feature="tokio-process"))]
            { Err("`sh-c:` is not supported in this Websocat build")? }
        } else
        if s.starts_with("exec:") {
            #[cfg(feature="tokio-process")]
            { boxup(super::process_peer::Exec(s[5..].into())) }
            #[cfg(not(feature="tokio-process"))]
            { Err("`exec:` is not supported in this Websocat build")? }
        } else {
            error!("Invalid specifier string `{}`", s);
            Err("Wrong specifier")?
        }
    }
}
<commit_msg>remove one stale TODO<commit_after>use websocket::client::Url;
use super::{Specifier,Result};
use std::rc::Rc;

pub fn ws_l_prefix(s:&str) -> Option<&str> {
    if    s.starts_with("ws-l:")
       || s.starts_with("l-ws:")
    {
        Some(&s[5..])
    }
    else if  s.starts_with("ws-listen:")
          || s.starts_with("listen-ws:")
    {
        Some(&s[10..])
    } else {
        None
    }
}

pub fn ws_c_prefix(s:&str) -> Option<&str> {
    if    s.starts_with("ws-c:")
       || s.starts_with("c-ws:")
    {
        Some(&s[5..])
    }
    else if  s.starts_with("ws-connect:")
          || s.starts_with("connect-ws:")
    {
        Some(&s[11..])
    } else {
        None
    }
}

pub fn reuser_prefix(s:&str) -> Option<&str> {
    if s.starts_with("reuse:") {
        Some(&s[6..])
    } else {
        None
    }
}

pub fn ws_url_prefix(s:&str) -> Option<&str> {
    if s.starts_with("ws://") {
        Some(s)
    } else
    if s.starts_with("wss://") {
        Some(s)
    } else {
        None
    }
}

pub fn unix_specific_prefix(s:&str) -> Option<&str> {
    if s.starts_with("unix") {
        Some(s)
    } else
    if s.starts_with("abstract") {
        #[cfg(not(any(target_os = "linux", target_os = "android")))]
        {
            warn!("Abstract-namespaced UNIX sockets are unlikely to be supported here");
        }
        Some(s)
    } else {
        None
    }
}

pub fn boxup<T:Specifier+'static>(x:T) -> Result<Rc<Specifier>> {
    Ok(Rc::new(x))
}

pub fn spec(s : &str) -> Result<Rc<Specifier>>  {
    Specifier::from_str(s)
}

impl Specifier {
    fn from_str(s: &str) -> Result<Rc<Specifier>> {
        if s == "-" || s == "inetd:" || s == "stdio:" {
            #[cfg(all(unix,not(feature="no_unix_stdio")))]
            {
                boxup(super::stdio_peer::Stdio)
            }
            #[cfg(any(not(unix),feature="no_unix_stdio"))]
            {
                boxup(super::stdio_threaded_peer::ThreadedStdio)
            }
        } else
        if s == "threadedstdio:" {
            boxup(super::stdio_threaded_peer::ThreadedStdio)
        } else
        if s == "mirror:" {
            boxup(super::mirror_peer::Mirror)
        } else
        if s == "clogged:" {
            boxup(super::trivial_peer::Clogged)
        } else
        if s.starts_with("literal:"){
            boxup(super::trivial_peer::Literal(s[8..].as_bytes().to_vec()))
        } else
        if s.starts_with("literalreply:"){
            boxup(super::mirror_peer::LiteralReply(s[13..].as_bytes().to_vec()))
        } else
        if s.starts_with("assert:"){
            boxup(super::trivial_peer::Assert(s[7..].as_bytes().to_vec()))
        } else
        if s.starts_with("tcp:") {
            boxup(super::net_peer::TcpConnect(s[4..].parse()?))
        } else
        if s.starts_with("tcp-connect:") {
            boxup(super::net_peer::TcpConnect(s[12..].parse()?))
        } else
        if s.starts_with("connect-tcp:") {
            boxup(super::net_peer::TcpConnect(s[12..].parse()?))
        } else
        if s.starts_with("c-tcp:") {
            boxup(super::net_peer::TcpConnect(s[6..].parse()?))
        } else
        if s.starts_with("tcp-c:") {
            boxup(super::net_peer::TcpConnect(s[6..].parse()?))
        } else
        if s.starts_with("tcp-l:") {
            boxup(super::net_peer::TcpListen(s[6..].parse()?))
        } else
        if s.starts_with("l-tcp:") {
            boxup(super::net_peer::TcpListen(s[6..].parse()?))
        } else
        if s.starts_with("tcp-listen:") {
            boxup(super::net_peer::TcpListen(s[11..].parse()?))
        } else
        if s.starts_with("listen-tcp:") {
            boxup(super::net_peer::TcpListen(s[11..].parse()?))
        } else
        if s.starts_with("udp:") {
            boxup(super::net_peer::UdpConnect(s[4..].parse()?))
        } else
        if s.starts_with("udp-connect:") {
            boxup(super::net_peer::UdpConnect(s[12..].parse()?))
        } else
        if s.starts_with("connect-udp:") {
            boxup(super::net_peer::UdpConnect(s[12..].parse()?))
        } else
        if s.starts_with("udp-c:") {
            boxup(super::net_peer::UdpConnect(s[6..].parse()?))
        } else
        if s.starts_with("c-udp:") {
            boxup(super::net_peer::UdpConnect(s[6..].parse()?))
        } else
        if s.starts_with("udp-listen:") {
            boxup(super::net_peer::UdpListen(s[11..].parse()?))
        } else
        if s.starts_with("listen-udp:") {
            boxup(super::net_peer::UdpListen(s[11..].parse()?))
        } else
        if s.starts_with("udp-l:") {
            boxup(super::net_peer::UdpListen(s[6..].parse()?))
        } else
        if s.starts_with("l-udp:") {
            boxup(super::net_peer::UdpListen(s[6..].parse()?))
        } else
        if let Some(x) = ws_l_prefix(s) {
            if x == "" {
                Err("Specify underlying protocol for ws-l:")?;
            }
            if let Some(c) = x.chars().next() {
                if c.is_numeric() || c == '[' {
                    // Assuming user uses old format like ws-l:127.0.0.1:8080
                    return spec(&("ws-l:tcp-l:".to_owned() + x));
                }
            }
            boxup(super::ws_server_peer::WsUpgrade(spec(x)?))
        } else
        if let Some(x) = ws_c_prefix(s) {
            boxup(super::ws_client_peer::WsConnect(spec(x)?))
        } else
        if let Some(x) = reuser_prefix(s) {
            boxup(super::connection_reuse_peer::Reuser(spec(x)?))
        } else
        if let Some(url_s) = ws_url_prefix(s) {
            let url : Url = url_s.parse()?;
            boxup(super::ws_client_peer::WsClient(url))
        } else
        if s.starts_with("autoreconnect:") {
            boxup(super::reconnect_peer::AutoReconnect(spec(&s[14..])?))
        } else
        if s.starts_with("open:") {
            return Err("There is no `open:` specifier. Consider `open-async:` or `readfile:` or `writefile:`")?;
        } else
        if s.starts_with("open-async:") {
            #[cfg(all(unix,not(feature="no_unix_stdio")))]
            {
                boxup(super::stdio_peer::OpenAsync(s[11..].into()))
            }
            #[cfg(any(not(unix),feature="no_unix_stdio"))]
            {
                Err("`open-async:` is not supported in this Websocat build")?
            }
        } else
        if s.starts_with("open-fd:") {
            #[cfg(all(unix,not(feature="no_unix_stdio")))]
            {
                boxup(super::stdio_peer::OpenFdAsync(s[8..].parse()?))
            }
            #[cfg(any(not(unix),feature="no_unix_stdio"))]
            {
                Err("`open-fd:` is not supported in this Websocat build")?
            }
        } else
        if s.starts_with("readfile:") {
            boxup(super::file_peer::ReadFile(s[9..].into()))
        } else
        if s.starts_with("writefile:") {
            boxup(super::file_peer::WriteFile(s[10..].into()))
        } else
        if let Some(ss) = unix_specific_prefix(s) {
            #[cfg(unix)]
            {
                if ss.starts_with("unix:") {
                    boxup(super::unix_peer::UnixConnect(s[5..].into()))
                } else
                if ss.starts_with("unix-connect:") {
                    boxup(super::unix_peer::UnixConnect(s[13..].into()))
                } else
                if ss.starts_with("connect-unix:") {
                    boxup(super::unix_peer::UnixConnect(s[13..].into()))
                } else
                if ss.starts_with("unix-c:") {
                    boxup(super::unix_peer::UnixConnect(s[7..].into()))
                } else
                if ss.starts_with("c-unix:") {
                    boxup(super::unix_peer::UnixConnect(s[7..].into()))
                } else
                if ss.starts_with("unix-listen:") {
                    boxup(super::unix_peer::UnixListen(s[12..].into()))
                } else
                if ss.starts_with("unix-l:") {
                    boxup(super::unix_peer::UnixListen(s[7..].into()))
                } else
                if ss.starts_with("listen-unix:") {
                    boxup(super::unix_peer::UnixListen(s[12..].into()))
                } else
                if ss.starts_with("l-unix:") {
                    boxup(super::unix_peer::UnixListen(s[7..].into()))
                } else
                if ss.starts_with("unix-dgram:") || ss.starts_with("dgram-unix:") {
                    let splits : Vec<&str> = s[11..].split(":").collect();
                    if splits.len() != 2 {
                        Err("Expected two colon-separted paths")?;
                    }
                    boxup(super::unix_peer::UnixDgram(splits[0].into(),splits[1].into()))
                } else
                if ss.starts_with("abstract:") {
                    boxup(super::unix_peer::AbstractConnect(s[9..].into()))
                } else
                if ss.starts_with("abstract-connect:") {
                    boxup(super::unix_peer::AbstractConnect(s[17..].into()))
                } else
                if ss.starts_with("connect-abstract:") {
                    boxup(super::unix_peer::AbstractConnect(s[17..].into()))
                } else
                if ss.starts_with("abstract-c:") {
                    boxup(super::unix_peer::AbstractConnect(s[11..].into()))
                } else
                if ss.starts_with("c-abstract:") {
                    boxup(super::unix_peer::AbstractConnect(s[11..].into()))
                } else
                if ss.starts_with("abstract-listen:") {
                    boxup(super::unix_peer::AbstractListen(s[16..].into()))
                } else
                if ss.starts_with("abstract-l:") {
                    boxup(super::unix_peer::AbstractListen(s[11..].into()))
                } else
                if ss.starts_with("listen-abstract:") {
                    boxup(super::unix_peer::AbstractListen(s[16..].into()))
                } else
                if ss.starts_with("l-abstract:") {
                    boxup(super::unix_peer::AbstractListen(s[11..].into()))
                } else
                if ss.starts_with("abstract-dgram:") || ss.starts_with("dgram-abstract:") {
                    let splits : Vec<&str> = s[15..].split(":").collect();
                    if splits.len() != 2 {
                        Err("Expected two colon-separted addresses")?;
                    }
                    boxup(super::unix_peer::AbstractDgram(splits[0].into(),splits[1].into()))
                } else
                {
                    error!("Invalid specifier string `{}`", ss);
                    Err("Wrong specifier")?
                }
            }
            #[cfg(not(unix))]
            {
                let _ss = ss;
                Err("`unix*:` or `abstract*:` are not supported in this Websocat build")?
            }
        } else
        if s == "inetd-ws:" {
            spec("ws-l:inetd:")
        } else
        if s.starts_with("l-ws-unix:") {
            spec(&format!("ws-l:unix-l:{}", &s[10..]))
        } else
        if s.starts_with("l-ws-abstract:") {
            spec(&format!("ws-l:abstract-l::{}", &s[14..]))
        } else
        if s.starts_with("sh-c:") {
            #[cfg(feature="tokio-process")]
            { boxup(super::process_peer::ShC(s[5..].into())) }
            #[cfg(not(feature="tokio-process"))]
            { Err("`sh-c:` is not supported in this Websocat build")? }
        } else
        if s.starts_with("exec:") {
            #[cfg(feature="tokio-process")]
            { boxup(super::process_peer::Exec(s[5..].into())) }
            #[cfg(not(feature="tokio-process"))]
            { Err("`exec:` is not supported in this Websocat build")? }
        } else {
            error!("Invalid specifier string `{}`", s);
            Err("Wrong specifier")?
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::{self, Ordering};
use std::f32;
use std::convert::From;
use rand::Rng;
use std::ops::Sub;
use super::selection::tournament_selection_fast;

pub trait Mate<T> {
    fn mate<R: Rng>(&mut self, rng: &mut R, p1: &T, p2: &T) -> T;
}

pub trait Dominate<Rhs=Self> {
    fn dominates(&self, other: &Rhs) -> bool;
}

pub trait MultiObjective {
    fn num_objectives() -> usize;

    fn cmp_objective(&self, other: &Self, objective: usize) -> Ordering;

    // Distance between self and other
    fn dist_objective(&self, other: &Self, objective: usize) -> f32;

}

impl<T: MultiObjective> Dominate<T> for T {
    fn dominates(&self, other: &Self) -> bool {
        let mut less_cnt = 0;
        for i in 0..Self::num_objectives() {
            match self.cmp_objective(other, i) {
                Ordering::Greater => {
                    return false;
                }
                Ordering::Less => {
                    less_cnt += 1;
                }
                Ordering::Equal => {
                }
            }
        }
        return less_cnt > 0;
    }
}

#[derive(Debug, Clone)]
pub struct MultiObjective2<T>
    where T: Sized + PartialOrd + Copy + Clone
{
    pub objectives: [T; 2],
}

impl<T: Sized + PartialOrd + Copy + Clone> From<(T, T)> for MultiObjective2<T> {
    fn from(t: (T, T)) -> MultiObjective2<T> {
        MultiObjective2 { objectives: [t.0, t.1] }
    }
}

impl<T, R> MultiObjective for MultiObjective2<T>
    where T: Copy + PartialOrd + Sub<Output = R>,
          R: Into<f32>
{
    fn num_objectives() -> usize {
        2
    }
    fn cmp_objective(&self, other: &Self, objective: usize) -> Ordering {
        self.objectives[objective].partial_cmp(&other.objectives[objective]).unwrap()
    }
    fn dist_objective(&self, other: &Self, objective: usize) -> f32 {
        (self.objectives[objective] - other.objectives[objective]).into()
    }
}

#[derive(Debug, Clone)]
pub struct MultiObjective3<T>
    where T: Sized + PartialOrd + Copy + Clone
{
    pub objectives: [T; 3],
}

impl<T: Sized + PartialOrd + Copy + Clone> From<(T, T, T)> for MultiObjective3<T> {
    fn from(t: (T, T, T)) -> MultiObjective3<T> {
        MultiObjective3 { objectives: [t.0, t.1, t.2] }
    }
}

impl<T, R> MultiObjective for MultiObjective3<T>
    where T: Copy + PartialOrd + Sub<Output = R>,
          R: Into<f32>
{
    fn num_objectives() -> usize {
        3
    }
    fn cmp_objective(&self, other: &Self, objective: usize) -> Ordering {
        self.objectives[objective].partial_cmp(&other.objectives[objective]).unwrap()
    }
    fn dist_objective(&self, other: &Self, objective: usize) -> f32 {
        (self.objectives[objective] - other.objectives[objective]).into()
    }
}

/// Stop after we have found `n` solutions (we include the whole pareto front, so it are probably more solutions).
fn fast_non_dominated_sort<P: Dominate>(solutions: &[P], n: usize) -> Vec<Vec<usize>> {
    let mut fronts: Vec<Vec<usize>> = Vec::new();
    let mut current_front = Vec::new();

    let mut domination_count: Vec<usize> = (0..solutions.len()).map(|_| 0).collect();
    let mut dominated_solutions: Vec<Vec<usize>> = (0..solutions.len())
                                                       .map(|_| Vec::new())
                                                       .collect();
    let mut found_solutions: usize = 0;

    for (i, p) in solutions.iter().enumerate() {
        for (j, q) in solutions.iter().enumerate() {
            if i == j {
                continue;
            }
            if p.dominates(q) {
                // Add `q` to the set of solutions dominated by `p`.
                dominated_solutions[i].push(j);
            } else if q.dominates(p) {
                // Increment domination counter of `p`.
                domination_count[i] += 1;
            }
        }

        if domination_count[i] == 0 {
            // `p` belongs to the first front as it is not dominated by any
            // other solution.
            current_front.push(i);
        }
    }

    while !current_front.is_empty() {
        found_solutions += current_front.len();
        if found_solutions >= n {
            fronts.push(current_front);
            break;
        } else {
            // we haven't found enough solutions yet.
            let mut next_front = Vec::new();
            for &p_i in current_front.iter() {
                for &q_i in dominated_solutions[p_i].iter() {
                    domination_count[q_i] -= 1;
                    if domination_count[q_i] == 0 {
                        // q belongs to the next front
                        next_front.push(q_i);
                    }
                }
            }
            fronts.push(current_front);
            current_front = next_front;
        }
    }

    return fronts;
}

#[derive(Debug)]
pub struct SolutionRankDist {
    pub idx: usize,
    pub rank: u32,
    pub dist: f32,
}

impl PartialEq for SolutionRankDist {
    #[inline]
    fn eq(&self, other: &SolutionRankDist) -> bool {
        self.rank == other.rank && self.dist == other.dist
    }
}

// Implement the crowding-distance comparison operator.
impl PartialOrd for SolutionRankDist {
    #[inline]
    // compare on rank first (ASC), then on dist (DESC)
    fn partial_cmp(&self, other: &SolutionRankDist) -> Option<Ordering> {
        match self.rank.partial_cmp(&other.rank) {
            Some(Ordering::Equal) => {
                // first criterion equal, second criterion decides
                // reverse ordering
                self.dist.partial_cmp(&other.dist).map(|i| i.reverse())
            }
            other => other,
        }
    }
}

fn crowding_distance_assignment<P: MultiObjective>(solutions: &[P],
                                                   common_rank: u32,
                                                   individuals_idx: &[usize])
                                                   -> Vec<SolutionRankDist> {
    let l = individuals_idx.len();
    let mut distance: Vec<f32> = (0..l).map(|_| 0.0).collect();
    let mut indices: Vec<usize> = (0..l).map(|i| i).collect();

    for m in 0..P::num_objectives() {
        // sort using objective `m`
        indices.sort_by(|&a, &b| {
            solutions[individuals_idx[a]].cmp_objective(&solutions[individuals_idx[b]], m)
        });
        distance[indices[0]] = f32::INFINITY;
        distance[indices[l - 1]] = f32::INFINITY;

        let min_idx = individuals_idx[indices[0]];
        let max_idx = individuals_idx[indices[l - 1]];

        let dist_max_min = solutions[max_idx].dist_objective(&solutions[min_idx], m);
        if dist_max_min != 0.0 {
            let norm = P::num_objectives() as f32 * dist_max_min;
            debug_assert!(norm != 0.0);
            for i in 1..(l - 1) {
                let next_idx = individuals_idx[indices[i + 1]];
                let prev_idx = individuals_idx[indices[i - 1]];
                distance[indices[i]] += solutions[next_idx]
                                            .dist_objective(&solutions[prev_idx], m) /
                                        norm;
            }
        }
    }

    return indices.iter()
                  .map(|&i| {
                      SolutionRankDist {
                          idx: individuals_idx[i],
                          rank: common_rank,
                          dist: distance[i],
                      }
                  })
                  .collect();
}

/// Select `n` out of the `solutions`, assigning rank and distance.
pub fn select<P: Dominate + MultiObjective>(solutions: &[P], n: usize) -> Vec<SolutionRankDist> {
    let mut selection = Vec::with_capacity(cmp::min(solutions.len(), n));

    let pareto_fronts = fast_non_dominated_sort(solutions, n);

    for (rank, front) in pareto_fronts.iter().enumerate() {
        if selection.len() >= n {
            break;
        }
        let missing: usize = n - selection.len();

        let mut solution_rank_dist = crowding_distance_assignment(solutions,
                                                                  rank as u32,
                                                                  &front[..]);
        if solution_rank_dist.len() <= missing {
            // whole front fits into result.
            selection.extend(solution_rank_dist);
            assert!(selection.len() <= n);
        } else {
            // choose only best from this front, according to the crowding distance.
            solution_rank_dist.sort_by(|a, b| {
                debug_assert!(a.rank == b.rank);
                a.partial_cmp(b).unwrap()
            });
            selection.extend(solution_rank_dist.into_iter().take(missing));
            assert!(selection.len() == n);
            break;
        }
    }

    return selection;
}

pub trait FitnessEval<I, F:Dominate+MultiObjective+Clone> {
    fn fitness(&mut self, &[I]) -> Vec<F>;
}

pub fn iterate<R: Rng,
               I: Clone,
               F: Dominate + MultiObjective + Clone,
               T: Mate<I> + FitnessEval<I, F>>
    (rng: &mut R,
     population: Vec<I>,
     fitness: Vec<F>,
     pop_size: usize,
     offspring_size: usize,
     tournament_k: usize,
     toolbox: &mut T)
     -> (Vec<I>, Vec<F>) {
    assert!(tournament_k > 0);
    assert!(population.len() == fitness.len());

    // evaluate rank and crowding distance (using select()).
    let rank_dist = select(&fitness[..], pop_size);
    assert!(rank_dist.len() == pop_size);

    // create `offspring_size` new offspring using k-tournament (
    // select the best individual out of k randomly choosen individuals)
    let offspring: Vec<_> = (0..offspring_size)
                                .map(|_| {

                                    // first parent. k candidates
                                    let p1 = tournament_selection_fast(rng,
                                                                       |i1, i2| {
                                                                           rank_dist[i1] <
                                                                           rank_dist[i2]
                                                                       },
                                                                       rank_dist.len(),
                                                                       tournament_k);

                                    // second parent. k candidates
                                    let p2 = tournament_selection_fast(rng,
                                                                       |i1, i2| {
                                                                           rank_dist[i1] <
                                                                           rank_dist[i2]
                                                                       },
                                                                       rank_dist.len(),
                                                                       tournament_k);

                                    // cross-over the two parents and produce one child (throw away
                                    // second child XXX)

                                    // The potentially dominating individual is gives as first
                                    // parameter.
                                    let (p1, p2) = if rank_dist[p1] < rank_dist[p2] {
                                        (p1, p2)
                                    } else if rank_dist[p2] < rank_dist[p1] {
                                        (p2, p1)
                                    } else {
                                        (p1, p2)
                                    };

                                    toolbox.mate(rng, &population[p1], &population[p2])
                                })
                                .collect();

    assert!(offspring.len() == offspring_size);

    // evaluate fitness of offspring
    let fitness_offspring = toolbox.fitness(&offspring[..]);
    assert!(fitness_offspring.len() == offspring.len());

    // merge population and offspring, then select
    let mut new_pop = Vec::with_capacity(rank_dist.len() + offspring.len());
    let mut new_fit = Vec::with_capacity(rank_dist.len() + offspring.len());
    for rd in rank_dist {
        new_pop.push(population[rd.idx].clone());
        new_fit.push(fitness[rd.idx].clone());
    }

    new_pop.extend(offspring);
    new_fit.extend(fitness_offspring);

    assert!(new_pop.len() == pop_size + offspring_size);
    assert!(new_fit.len() == pop_size + offspring_size);

    return (new_pop, new_fit);
}

#[test]
fn test_dominates() {
    let a = MultiObjective2 { objectives: [1.0f32, 0.1] };
    let b = MultiObjective2 { objectives: [0.1f32, 0.1] };
    let c = MultiObjective2 { objectives: [0.1f32, 1.0] };

    assert_eq!(false, a.dominates(&a));
    assert_eq!(false, a.dominates(&b));
    assert_eq!(false, a.dominates(&c));

    assert_eq!(true, b.dominates(&a));
    assert_eq!(false, b.dominates(&b));
    assert_eq!(true, b.dominates(&c));

    assert_eq!(false, c.dominates(&a));
    assert_eq!(false, c.dominates(&b));
    assert_eq!(false, c.dominates(&c));
}

#[test]
fn test_abc() {
    let mut solutions: Vec<MultiObjective2<f32>> = Vec::new();
    solutions.push(MultiObjective2 { objectives: [1.0, 0.1] });
    solutions.push(MultiObjective2 { objectives: [0.1, 0.1] });
    solutions.push(MultiObjective2 { objectives: [0.1, 1.0] });
    solutions.push(MultiObjective2 { objectives: [0.5, 0.5] });
    solutions.push(MultiObjective2 { objectives: [0.5, 0.5] });

    println!("solutions: {:?}", solutions);
    let selection = select(&solutions[..], 5);
    println!("selection: {:?}", selection);

    let fronts = fast_non_dominated_sort(&solutions[..], 10);
    println!("solutions: {:?}", solutions);
    println!("fronts: {:?}", fronts);

    for (rank, front) in fronts.iter().enumerate() {
        let distances = crowding_distance_assignment(&solutions[..], rank as u32, &front[..]);
        println!("front: {:?}", front);
        println!("distances: {:?}", distances);
    }
}
<commit_msg>Add MultiObjective4<commit_after>use std::cmp::{self, Ordering};
use std::f32;
use std::convert::From;
use rand::Rng;
use std::ops::Sub;
use super::selection::tournament_selection_fast;

pub trait Mate<T> {
    fn mate<R: Rng>(&mut self, rng: &mut R, p1: &T, p2: &T) -> T;
}

pub trait Dominate<Rhs=Self> {
    fn dominates(&self, other: &Rhs) -> bool;
}

pub trait MultiObjective {
    fn num_objectives() -> usize;

    fn cmp_objective(&self, other: &Self, objective: usize) -> Ordering;

    // Distance between self and other
    fn dist_objective(&self, other: &Self, objective: usize) -> f32;

}

impl<T: MultiObjective> Dominate<T> for T {
    fn dominates(&self, other: &Self) -> bool {
        let mut less_cnt = 0;
        for i in 0..Self::num_objectives() {
            match self.cmp_objective(other, i) {
                Ordering::Greater => {
                    return false;
                }
                Ordering::Less => {
                    less_cnt += 1;
                }
                Ordering::Equal => {
                }
            }
        }
        return less_cnt > 0;
    }
}

#[derive(Debug, Clone)]
pub struct MultiObjective2<T>
    where T: Sized + PartialOrd + Copy + Clone
{
    pub objectives: [T; 2],
}

impl<T: Sized + PartialOrd + Copy + Clone> From<(T, T)> for MultiObjective2<T> {
    fn from(t: (T, T)) -> MultiObjective2<T> {
        MultiObjective2 { objectives: [t.0, t.1] }
    }
}

impl<T, R> MultiObjective for MultiObjective2<T>
    where T: Copy + PartialOrd + Sub<Output = R>,
          R: Into<f32>
{
    fn num_objectives() -> usize {
        2
    }
    fn cmp_objective(&self, other: &Self, objective: usize) -> Ordering {
        self.objectives[objective].partial_cmp(&other.objectives[objective]).unwrap()
    }
    fn dist_objective(&self, other: &Self, objective: usize) -> f32 {
        (self.objectives[objective] - other.objectives[objective]).into()
    }
}

#[derive(Debug, Clone)]
pub struct MultiObjective3<T>
    where T: Sized + PartialOrd + Copy + Clone
{
    pub objectives: [T; 3],
}

impl<T: Sized + PartialOrd + Copy + Clone> From<(T, T, T)> for MultiObjective3<T> {
    fn from(t: (T, T, T)) -> MultiObjective3<T> {
        MultiObjective3 { objectives: [t.0, t.1, t.2] }
    }
}

impl<T, R> MultiObjective for MultiObjective3<T>
    where T: Copy + PartialOrd + Sub<Output = R>,
          R: Into<f32>
{
    fn num_objectives() -> usize {
        3
    }
    fn cmp_objective(&self, other: &Self, objective: usize) -> Ordering {
        self.objectives[objective].partial_cmp(&other.objectives[objective]).unwrap()
    }
    fn dist_objective(&self, other: &Self, objective: usize) -> f32 {
        (self.objectives[objective] - other.objectives[objective]).into()
    }
}

#[derive(Debug, Clone)]
pub struct MultiObjective4<T>
    where T: Sized + PartialOrd + Copy + Clone
{
    pub objectives: [T; 4],
}

impl<T: Sized + PartialOrd + Copy + Clone> From<(T, T, T, T)> for MultiObjective4<T> {
    fn from(t: (T, T, T, T)) -> MultiObjective4<T> {
        MultiObjective4 { objectives: [t.0, t.1, t.2, t.3] }
    }
}

impl<T, R> MultiObjective for MultiObjective4<T>
    where T: Copy + PartialOrd + Sub<Output = R>,
          R: Into<f32>
{
    fn num_objectives() -> usize {
        4
    }
    fn cmp_objective(&self, other: &Self, objective: usize) -> Ordering {
        self.objectives[objective].partial_cmp(&other.objectives[objective]).unwrap()
    }
    fn dist_objective(&self, other: &Self, objective: usize) -> f32 {
        (self.objectives[objective] - other.objectives[objective]).into()
    }
}

/// Stop after we have found `n` solutions (we include the whole pareto front, so it are probably more solutions).
fn fast_non_dominated_sort<P: Dominate>(solutions: &[P], n: usize) -> Vec<Vec<usize>> {
    let mut fronts: Vec<Vec<usize>> = Vec::new();
    let mut current_front = Vec::new();

    let mut domination_count: Vec<usize> = (0..solutions.len()).map(|_| 0).collect();
    let mut dominated_solutions: Vec<Vec<usize>> = (0..solutions.len())
                                                       .map(|_| Vec::new())
                                                       .collect();
    let mut found_solutions: usize = 0;

    for (i, p) in solutions.iter().enumerate() {
        for (j, q) in solutions.iter().enumerate() {
            if i == j {
                continue;
            }
            if p.dominates(q) {
                // Add `q` to the set of solutions dominated by `p`.
                dominated_solutions[i].push(j);
            } else if q.dominates(p) {
                // Increment domination counter of `p`.
                domination_count[i] += 1;
            }
        }

        if domination_count[i] == 0 {
            // `p` belongs to the first front as it is not dominated by any
            // other solution.
            current_front.push(i);
        }
    }

    while !current_front.is_empty() {
        found_solutions += current_front.len();
        if found_solutions >= n {
            fronts.push(current_front);
            break;
        } else {
            // we haven't found enough solutions yet.
            let mut next_front = Vec::new();
            for &p_i in current_front.iter() {
                for &q_i in dominated_solutions[p_i].iter() {
                    domination_count[q_i] -= 1;
                    if domination_count[q_i] == 0 {
                        // q belongs to the next front
                        next_front.push(q_i);
                    }
                }
            }
            fronts.push(current_front);
            current_front = next_front;
        }
    }

    return fronts;
}

#[derive(Debug)]
pub struct SolutionRankDist {
    pub idx: usize,
    pub rank: u32,
    pub dist: f32,
}

impl PartialEq for SolutionRankDist {
    #[inline]
    fn eq(&self, other: &SolutionRankDist) -> bool {
        self.rank == other.rank && self.dist == other.dist
    }
}

// Implement the crowding-distance comparison operator.
impl PartialOrd for SolutionRankDist {
    #[inline]
    // compare on rank first (ASC), then on dist (DESC)
    fn partial_cmp(&self, other: &SolutionRankDist) -> Option<Ordering> {
        match self.rank.partial_cmp(&other.rank) {
            Some(Ordering::Equal) => {
                // first criterion equal, second criterion decides
                // reverse ordering
                self.dist.partial_cmp(&other.dist).map(|i| i.reverse())
            }
            other => other,
        }
    }
}

fn crowding_distance_assignment<P: MultiObjective>(solutions: &[P],
                                                   common_rank: u32,
                                                   individuals_idx: &[usize])
                                                   -> Vec<SolutionRankDist> {
    let l = individuals_idx.len();
    let mut distance: Vec<f32> = (0..l).map(|_| 0.0).collect();
    let mut indices: Vec<usize> = (0..l).map(|i| i).collect();

    for m in 0..P::num_objectives() {
        // sort using objective `m`
        indices.sort_by(|&a, &b| {
            solutions[individuals_idx[a]].cmp_objective(&solutions[individuals_idx[b]], m)
        });
        distance[indices[0]] = f32::INFINITY;
        distance[indices[l - 1]] = f32::INFINITY;

        let min_idx = individuals_idx[indices[0]];
        let max_idx = individuals_idx[indices[l - 1]];

        let dist_max_min = solutions[max_idx].dist_objective(&solutions[min_idx], m);
        if dist_max_min != 0.0 {
            let norm = P::num_objectives() as f32 * dist_max_min;
            debug_assert!(norm != 0.0);
            for i in 1..(l - 1) {
                let next_idx = individuals_idx[indices[i + 1]];
                let prev_idx = individuals_idx[indices[i - 1]];
                distance[indices[i]] += solutions[next_idx]
                                            .dist_objective(&solutions[prev_idx], m) /
                                        norm;
            }
        }
    }

    return indices.iter()
                  .map(|&i| {
                      SolutionRankDist {
                          idx: individuals_idx[i],
                          rank: common_rank,
                          dist: distance[i],
                      }
                  })
                  .collect();
}

/// Select `n` out of the `solutions`, assigning rank and distance.
pub fn select<P: Dominate + MultiObjective>(solutions: &[P], n: usize) -> Vec<SolutionRankDist> {
    let mut selection = Vec::with_capacity(cmp::min(solutions.len(), n));

    let pareto_fronts = fast_non_dominated_sort(solutions, n);

    for (rank, front) in pareto_fronts.iter().enumerate() {
        if selection.len() >= n {
            break;
        }
        let missing: usize = n - selection.len();

        let mut solution_rank_dist = crowding_distance_assignment(solutions,
                                                                  rank as u32,
                                                                  &front[..]);
        if solution_rank_dist.len() <= missing {
            // whole front fits into result.
            selection.extend(solution_rank_dist);
            assert!(selection.len() <= n);
        } else {
            // choose only best from this front, according to the crowding distance.
            solution_rank_dist.sort_by(|a, b| {
                debug_assert!(a.rank == b.rank);
                a.partial_cmp(b).unwrap()
            });
            selection.extend(solution_rank_dist.into_iter().take(missing));
            assert!(selection.len() == n);
            break;
        }
    }

    return selection;
}

pub trait FitnessEval<I, F:Dominate+MultiObjective+Clone> {
    fn fitness(&mut self, &[I]) -> Vec<F>;
}

pub fn iterate<R: Rng,
               I: Clone,
               F: Dominate + MultiObjective + Clone,
               T: Mate<I> + FitnessEval<I, F>>
    (rng: &mut R,
     population: Vec<I>,
     fitness: Vec<F>,
     pop_size: usize,
     offspring_size: usize,
     tournament_k: usize,
     toolbox: &mut T)
     -> (Vec<I>, Vec<F>) {
    assert!(tournament_k > 0);
    assert!(population.len() == fitness.len());

    // evaluate rank and crowding distance (using select()).
    let rank_dist = select(&fitness[..], pop_size);
    assert!(rank_dist.len() == pop_size);

    // create `offspring_size` new offspring using k-tournament (
    // select the best individual out of k randomly choosen individuals)
    let offspring: Vec<_> = (0..offspring_size)
                                .map(|_| {

                                    // first parent. k candidates
                                    let p1 = tournament_selection_fast(rng,
                                                                       |i1, i2| {
                                                                           rank_dist[i1] <
                                                                           rank_dist[i2]
                                                                       },
                                                                       rank_dist.len(),
                                                                       tournament_k);

                                    // second parent. k candidates
                                    let p2 = tournament_selection_fast(rng,
                                                                       |i1, i2| {
                                                                           rank_dist[i1] <
                                                                           rank_dist[i2]
                                                                       },
                                                                       rank_dist.len(),
                                                                       tournament_k);

                                    // cross-over the two parents and produce one child (throw away
                                    // second child XXX)

                                    // The potentially dominating individual is gives as first
                                    // parameter.
                                    let (p1, p2) = if rank_dist[p1] < rank_dist[p2] {
                                        (p1, p2)
                                    } else if rank_dist[p2] < rank_dist[p1] {
                                        (p2, p1)
                                    } else {
                                        (p1, p2)
                                    };

                                    toolbox.mate(rng, &population[p1], &population[p2])
                                })
                                .collect();

    assert!(offspring.len() == offspring_size);

    // evaluate fitness of offspring
    let fitness_offspring = toolbox.fitness(&offspring[..]);
    assert!(fitness_offspring.len() == offspring.len());

    // merge population and offspring, then select
    let mut new_pop = Vec::with_capacity(rank_dist.len() + offspring.len());
    let mut new_fit = Vec::with_capacity(rank_dist.len() + offspring.len());
    for rd in rank_dist {
        new_pop.push(population[rd.idx].clone());
        new_fit.push(fitness[rd.idx].clone());
    }

    new_pop.extend(offspring);
    new_fit.extend(fitness_offspring);

    assert!(new_pop.len() == pop_size + offspring_size);
    assert!(new_fit.len() == pop_size + offspring_size);

    return (new_pop, new_fit);
}

#[test]
fn test_dominates() {
    let a = MultiObjective2 { objectives: [1.0f32, 0.1] };
    let b = MultiObjective2 { objectives: [0.1f32, 0.1] };
    let c = MultiObjective2 { objectives: [0.1f32, 1.0] };

    assert_eq!(false, a.dominates(&a));
    assert_eq!(false, a.dominates(&b));
    assert_eq!(false, a.dominates(&c));

    assert_eq!(true, b.dominates(&a));
    assert_eq!(false, b.dominates(&b));
    assert_eq!(true, b.dominates(&c));

    assert_eq!(false, c.dominates(&a));
    assert_eq!(false, c.dominates(&b));
    assert_eq!(false, c.dominates(&c));
}

#[test]
fn test_abc() {
    let mut solutions: Vec<MultiObjective2<f32>> = Vec::new();
    solutions.push(MultiObjective2 { objectives: [1.0, 0.1] });
    solutions.push(MultiObjective2 { objectives: [0.1, 0.1] });
    solutions.push(MultiObjective2 { objectives: [0.1, 1.0] });
    solutions.push(MultiObjective2 { objectives: [0.5, 0.5] });
    solutions.push(MultiObjective2 { objectives: [0.5, 0.5] });

    println!("solutions: {:?}", solutions);
    let selection = select(&solutions[..], 5);
    println!("selection: {:?}", selection);

    let fronts = fast_non_dominated_sort(&solutions[..], 10);
    println!("solutions: {:?}", solutions);
    println!("fronts: {:?}", fronts);

    for (rank, front) in fronts.iter().enumerate() {
        let distances = crowding_distance_assignment(&solutions[..], rank as u32, &front[..]);
        println!("front: {:?}", front);
        println!("distances: {:?}", distances);
    }
}
<|endoftext|>