mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-31 06:22:00 +00:00
Remove a number of deprecated crates
All of these crates have been deprecated for some time and properly live in the rust-lang organization as cargo-based crates. To update your code, depend on the rust-lang/foo repository via cargo. [breaking-change]
This commit is contained in:
parent
c121cbab35
commit
fb169d5543
13
mk/crates.mk
13
mk/crates.mk
@ -49,11 +49,11 @@
|
||||
# automatically generated for all stage/host/target combinations.
|
||||
################################################################################
|
||||
|
||||
TARGET_CRATES := libc std green native flate arena glob term semver \
|
||||
uuid serialize sync getopts collections num test time rand \
|
||||
url log regex graphviz core rbml rlibc alloc rustrt \
|
||||
TARGET_CRATES := libc std green native flate arena term \
|
||||
serialize sync getopts collections test time rand \
|
||||
log regex graphviz core rbml rlibc alloc rustrt \
|
||||
unicode
|
||||
HOST_CRATES := syntax rustc rustdoc fourcc hexfloat regex_macros fmt_macros \
|
||||
HOST_CRATES := syntax rustc rustdoc regex_macros fmt_macros \
|
||||
rustc_llvm rustc_back
|
||||
CRATES := $(TARGET_CRATES) $(HOST_CRATES)
|
||||
TOOLS := compiletest rustdoc rustc
|
||||
@ -83,18 +83,13 @@ DEPS_glob := std
|
||||
DEPS_serialize := std log
|
||||
DEPS_rbml := std log serialize
|
||||
DEPS_term := std log
|
||||
DEPS_semver := std
|
||||
DEPS_uuid := std serialize
|
||||
DEPS_sync := core alloc rustrt collections
|
||||
DEPS_getopts := std
|
||||
DEPS_collections := core alloc unicode
|
||||
DEPS_fourcc := rustc syntax std
|
||||
DEPS_hexfloat := rustc syntax std
|
||||
DEPS_num := std
|
||||
DEPS_test := std getopts serialize rbml term time regex native:rust_test_helpers
|
||||
DEPS_time := std serialize
|
||||
DEPS_rand := core
|
||||
DEPS_url := std
|
||||
DEPS_log := std regex
|
||||
DEPS_regex := std
|
||||
DEPS_regex_macros = rustc syntax std regex
|
||||
|
@ -1,163 +0,0 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
Syntax extension to generate FourCCs.
|
||||
|
||||
Once loaded, fourcc!() is called with a single 4-character string,
|
||||
and an optional ident that is either `big`, `little`, or `target`.
|
||||
The ident represents endianness, and specifies in which direction
|
||||
the characters should be read. If the ident is omitted, it is assumed
|
||||
to be `big`, i.e. left-to-right order. It returns a u32.
|
||||
|
||||
# Examples
|
||||
|
||||
To load the extension and use it:
|
||||
|
||||
```rust,ignore
|
||||
#[phase(plugin)]
|
||||
extern crate fourcc;
|
||||
|
||||
fn main() {
|
||||
let val = fourcc!("\xC0\xFF\xEE!");
|
||||
assert_eq!(val, 0xC0FFEE21u32);
|
||||
let little_val = fourcc!("foo ", little);
|
||||
assert_eq!(little_val, 0x21EEFFC0u32);
|
||||
}
|
||||
```
|
||||
|
||||
# References
|
||||
|
||||
* [Wikipedia: FourCC](http://en.wikipedia.org/wiki/FourCC)
|
||||
|
||||
*/
|
||||
|
||||
#![crate_name = "fourcc"]
|
||||
#![deprecated = "This is now a cargo package located at: \
|
||||
https://github.com/rust-lang/fourcc"]
|
||||
#![allow(deprecated)]
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
#![license = "MIT/ASL2"]
|
||||
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||
html_root_url = "http://doc.rust-lang.org/nightly/")]
|
||||
|
||||
#![feature(plugin_registrar)]
|
||||
|
||||
extern crate syntax;
|
||||
extern crate rustc;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::attr::contains;
|
||||
use syntax::codemap::{Span, mk_sp};
|
||||
use syntax::ext::base;
|
||||
use syntax::ext::base::{ExtCtxt, MacExpr};
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::parse::token;
|
||||
use syntax::parse::token::InternedString;
|
||||
use syntax::ptr::P;
|
||||
use rustc::plugin::Registry;
|
||||
|
||||
#[plugin_registrar]
|
||||
pub fn plugin_registrar(reg: &mut Registry) {
|
||||
reg.register_macro("fourcc", expand_syntax_ext);
|
||||
}
|
||||
|
||||
pub fn expand_syntax_ext<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
-> Box<base::MacResult+'cx> {
|
||||
let (expr, endian) = parse_tts(cx, tts);
|
||||
|
||||
let little = match endian {
|
||||
None => false,
|
||||
Some(Ident{ident, span}) => match token::get_ident(ident).get() {
|
||||
"little" => true,
|
||||
"big" => false,
|
||||
"target" => target_endian_little(cx, sp),
|
||||
_ => {
|
||||
cx.span_err(span, "invalid endian directive in fourcc!");
|
||||
target_endian_little(cx, sp)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let s = match expr.node {
|
||||
// expression is a literal
|
||||
ast::ExprLit(ref lit) => match lit.node {
|
||||
// string literal
|
||||
ast::LitStr(ref s, _) => {
|
||||
if s.get().char_len() != 4 {
|
||||
cx.span_err(expr.span, "string literal with len != 4 in fourcc!");
|
||||
}
|
||||
s
|
||||
}
|
||||
_ => {
|
||||
cx.span_err(expr.span, "unsupported literal in fourcc!");
|
||||
return base::DummyResult::expr(sp)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
cx.span_err(expr.span, "non-literal in fourcc!");
|
||||
return base::DummyResult::expr(sp)
|
||||
}
|
||||
};
|
||||
|
||||
let mut val = 0u32;
|
||||
for codepoint in s.get().chars().take(4) {
|
||||
let byte = if codepoint as u32 > 0xFF {
|
||||
cx.span_err(expr.span, "fourcc! literal character out of range 0-255");
|
||||
0u8
|
||||
} else {
|
||||
codepoint as u8
|
||||
};
|
||||
|
||||
val = if little {
|
||||
(val >> 8) | ((byte as u32) << 24)
|
||||
} else {
|
||||
(val << 8) | (byte as u32)
|
||||
};
|
||||
}
|
||||
let e = cx.expr_lit(sp, ast::LitInt(val as u64, ast::UnsignedIntLit(ast::TyU32)));
|
||||
MacExpr::new(e)
|
||||
}
|
||||
|
||||
struct Ident {
|
||||
ident: ast::Ident,
|
||||
span: Span
|
||||
}
|
||||
|
||||
fn parse_tts(cx: &ExtCtxt,
|
||||
tts: &[ast::TokenTree]) -> (P<ast::Expr>, Option<Ident>) {
|
||||
let p = &mut cx.new_parser_from_tts(tts);
|
||||
let ex = p.parse_expr();
|
||||
let id = if p.token == token::EOF {
|
||||
None
|
||||
} else {
|
||||
p.expect(&token::COMMA);
|
||||
let lo = p.span.lo;
|
||||
let ident = p.parse_ident();
|
||||
let hi = p.last_span.hi;
|
||||
Some(Ident{ident: ident, span: mk_sp(lo, hi)})
|
||||
};
|
||||
if p.token != token::EOF {
|
||||
p.unexpected();
|
||||
}
|
||||
(ex, id)
|
||||
}
|
||||
|
||||
fn target_endian_little(cx: &ExtCtxt, sp: Span) -> bool {
|
||||
let meta = cx.meta_name_value(sp, InternedString::new("target_endian"),
|
||||
ast::LitStr(InternedString::new("little"), ast::CookedStr));
|
||||
contains(cx.cfg().as_slice(), &*meta)
|
||||
}
|
||||
|
||||
// FIXME (10872): This is required to prevent an LLVM assert on Windows
|
||||
#[test]
|
||||
fn dummy_test() { }
|
@ -1,893 +0,0 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
* Support for matching file paths against Unix shell style patterns.
|
||||
*
|
||||
* The `glob` and `glob_with` functions, in concert with the `Paths`
|
||||
* type, allow querying the filesystem for all files that match a particular
|
||||
* pattern - just like the libc `glob` function (for an example see the `glob`
|
||||
* documentation). The methods on the `Pattern` type provide functionality
|
||||
* for checking if individual paths match a particular pattern - in a similar
|
||||
* manner to the libc `fnmatch` function
|
||||
*
|
||||
* For consistency across platforms, and for Windows support, this module
|
||||
* is implemented entirely in Rust rather than deferring to the libc
|
||||
* `glob`/`fnmatch` functions.
|
||||
*/
|
||||
|
||||
#![crate_name = "glob"]
|
||||
#![deprecated = "This is now a cargo package located at: \
|
||||
https://github.com/rust-lang/glob"]
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
#![license = "MIT/ASL2"]
|
||||
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||
html_root_url = "http://doc.rust-lang.org/nightly/",
|
||||
html_playground_url = "http://play.rust-lang.org/")]
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::{cmp, os, path};
|
||||
use std::io::fs::PathExtensions;
|
||||
use std::io::fs;
|
||||
use std::path::is_sep;
|
||||
use std::string::String;
|
||||
|
||||
/**
|
||||
* An iterator that yields Paths from the filesystem that match a particular
|
||||
* pattern - see the `glob` function for more details.
|
||||
*/
|
||||
pub struct Paths {
|
||||
dir_patterns: Vec<Pattern>,
|
||||
require_dir: bool,
|
||||
options: MatchOptions,
|
||||
todo: Vec<(Path,uint)>,
|
||||
}
|
||||
|
||||
///
|
||||
/// Return an iterator that produces all the Paths that match the given pattern,
|
||||
/// which may be absolute or relative to the current working directory.
|
||||
///
|
||||
/// This method uses the default match options and is equivalent to calling
|
||||
/// `glob_with(pattern, MatchOptions::new())`. Use `glob_with` directly if you
|
||||
/// want to use non-default match options.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Consider a directory `/media/pictures` containing only the files `kittens.jpg`,
|
||||
/// `puppies.jpg` and `hamsters.gif`:
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![allow(deprecated)]
|
||||
/// use glob::glob;
|
||||
///
|
||||
/// for path in glob("/media/pictures/*.jpg") {
|
||||
/// println!("{}", path.display());
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The above code will print:
|
||||
///
|
||||
/// ```ignore
|
||||
/// /media/pictures/kittens.jpg
|
||||
/// /media/pictures/puppies.jpg
|
||||
/// ```
|
||||
///
|
||||
pub fn glob(pattern: &str) -> Paths {
|
||||
glob_with(pattern, MatchOptions::new())
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator that produces all the Paths that match the given pattern,
|
||||
* which may be absolute or relative to the current working directory.
|
||||
*
|
||||
* This function accepts Unix shell style patterns as described by `Pattern::new(..)`.
|
||||
* The options given are passed through unchanged to `Pattern::matches_with(..)` with
|
||||
* the exception that `require_literal_separator` is always set to `true` regardless of the
|
||||
* value passed to this function.
|
||||
*
|
||||
* Paths are yielded in alphabetical order, as absolute paths.
|
||||
*/
|
||||
pub fn glob_with(pattern: &str, options: MatchOptions) -> Paths {
|
||||
#[cfg(windows)]
|
||||
fn check_windows_verbatim(p: &Path) -> bool { path::windows::is_verbatim(p) }
|
||||
#[cfg(not(windows))]
|
||||
fn check_windows_verbatim(_: &Path) -> bool { false }
|
||||
|
||||
// calculate root this way to handle volume-relative Windows paths correctly
|
||||
let mut root = os::getcwd();
|
||||
let pat_root = Path::new(pattern).root_path();
|
||||
if pat_root.is_some() {
|
||||
if check_windows_verbatim(pat_root.as_ref().unwrap()) {
|
||||
// FIXME: How do we want to handle verbatim paths? I'm inclined to return nothing,
|
||||
// since we can't very well find all UNC shares with a 1-letter server name.
|
||||
return Paths {
|
||||
dir_patterns: Vec::new(),
|
||||
require_dir: false,
|
||||
options: options,
|
||||
todo: Vec::new(),
|
||||
};
|
||||
}
|
||||
root.push(pat_root.as_ref().unwrap());
|
||||
}
|
||||
|
||||
let root_len = pat_root.map_or(0u, |p| p.as_vec().len());
|
||||
let dir_patterns = pattern.slice_from(cmp::min(root_len, pattern.len()))
|
||||
.split_terminator(is_sep)
|
||||
.map(|s| Pattern::new(s))
|
||||
.collect::<Vec<Pattern>>();
|
||||
let require_dir = pattern.chars().next_back().map(is_sep) == Some(true);
|
||||
|
||||
let mut todo = Vec::new();
|
||||
if dir_patterns.len() > 0 {
|
||||
// Shouldn't happen, but we're using -1 as a special index.
|
||||
assert!(dir_patterns.len() < -1 as uint);
|
||||
|
||||
fill_todo(&mut todo, dir_patterns.as_slice(), 0, &root, options);
|
||||
}
|
||||
|
||||
Paths {
|
||||
dir_patterns: dir_patterns,
|
||||
require_dir: require_dir,
|
||||
options: options,
|
||||
todo: todo,
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator<Path> for Paths {
|
||||
|
||||
fn next(&mut self) -> Option<Path> {
|
||||
loop {
|
||||
if self.dir_patterns.is_empty() || self.todo.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (path,idx) = self.todo.pop().unwrap();
|
||||
// idx -1: was already checked by fill_todo, maybe path was '.' or
|
||||
// '..' that we can't match here because of normalization.
|
||||
if idx == -1 as uint {
|
||||
if self.require_dir && !path.is_dir() { continue; }
|
||||
return Some(path);
|
||||
}
|
||||
let ref pattern = self.dir_patterns[idx];
|
||||
|
||||
if pattern.matches_with(match path.filename_str() {
|
||||
// this ugly match needs to go here to avoid a borrowck error
|
||||
None => {
|
||||
// FIXME (#9639): How do we handle non-utf8 filenames? Ignore them for now
|
||||
// Ideally we'd still match them against a *
|
||||
continue;
|
||||
}
|
||||
Some(x) => x
|
||||
}, self.options) {
|
||||
if idx == self.dir_patterns.len() - 1 {
|
||||
// it is not possible for a pattern to match a directory *AND* its children
|
||||
// so we don't need to check the children
|
||||
|
||||
if !self.require_dir || path.is_dir() {
|
||||
return Some(path);
|
||||
}
|
||||
} else {
|
||||
fill_todo(&mut self.todo, self.dir_patterns.as_slice(),
|
||||
idx + 1, &path, self.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn list_dir_sorted(path: &Path) -> Option<Vec<Path>> {
|
||||
match fs::readdir(path) {
|
||||
Ok(mut children) => {
|
||||
children.sort_by(|p1, p2| p2.filename().cmp(&p1.filename()));
|
||||
Some(children.into_iter().collect())
|
||||
}
|
||||
Err(..) => None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A compiled Unix shell style pattern.
|
||||
*/
|
||||
#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct Pattern {
|
||||
tokens: Vec<PatternToken>,
|
||||
}
|
||||
|
||||
#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
enum PatternToken {
|
||||
Char(char),
|
||||
AnyChar,
|
||||
AnySequence,
|
||||
AnyWithin(Vec<CharSpecifier> ),
|
||||
AnyExcept(Vec<CharSpecifier> )
|
||||
}
|
||||
|
||||
#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
enum CharSpecifier {
|
||||
SingleChar(char),
|
||||
CharRange(char, char)
|
||||
}
|
||||
|
||||
#[deriving(PartialEq)]
|
||||
enum MatchResult {
|
||||
Match,
|
||||
SubPatternDoesntMatch,
|
||||
EntirePatternDoesntMatch
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
|
||||
/**
|
||||
* This function compiles Unix shell style patterns: `?` matches any single
|
||||
* character, `*` matches any (possibly empty) sequence of characters and
|
||||
* `[...]` matches any character inside the brackets, unless the first
|
||||
* character is `!` in which case it matches any character except those
|
||||
* between the `!` and the `]`. Character sequences can also specify ranges
|
||||
* of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any
|
||||
* character between 0 and 9 inclusive.
|
||||
*
|
||||
* The metacharacters `?`, `*`, `[`, `]` can be matched by using brackets
|
||||
* (e.g. `[?]`). When a `]` occurs immediately following `[` or `[!` then
|
||||
* it is interpreted as being part of, rather then ending, the character
|
||||
* set, so `]` and NOT `]` can be matched by `[]]` and `[!]]` respectively.
|
||||
* The `-` character can be specified inside a character sequence pattern by
|
||||
* placing it at the start or the end, e.g. `[abc-]`.
|
||||
*
|
||||
* When a `[` does not have a closing `]` before the end of the string then
|
||||
* the `[` will be treated literally.
|
||||
*/
|
||||
pub fn new(pattern: &str) -> Pattern {
|
||||
|
||||
let chars = pattern.chars().collect::<Vec<_>>();
|
||||
let mut tokens = Vec::new();
|
||||
let mut i = 0;
|
||||
|
||||
while i < chars.len() {
|
||||
match chars[i] {
|
||||
'?' => {
|
||||
tokens.push(AnyChar);
|
||||
i += 1;
|
||||
}
|
||||
'*' => {
|
||||
// *, **, ***, ****, ... are all equivalent
|
||||
while i < chars.len() && chars[i] == '*' {
|
||||
i += 1;
|
||||
}
|
||||
tokens.push(AnySequence);
|
||||
}
|
||||
'[' => {
|
||||
|
||||
if i <= chars.len() - 4 && chars[i + 1] == '!' {
|
||||
match chars.slice_from(i + 3).position_elem(&']') {
|
||||
None => (),
|
||||
Some(j) => {
|
||||
let chars = chars.slice(i + 2, i + 3 + j);
|
||||
let cs = parse_char_specifiers(chars);
|
||||
tokens.push(AnyExcept(cs));
|
||||
i += j + 4;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if i <= chars.len() - 3 && chars[i + 1] != '!' {
|
||||
match chars.slice_from(i + 2).position_elem(&']') {
|
||||
None => (),
|
||||
Some(j) => {
|
||||
let cs = parse_char_specifiers(chars.slice(i + 1, i + 2 + j));
|
||||
tokens.push(AnyWithin(cs));
|
||||
i += j + 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we get here then this is not a valid range pattern
|
||||
tokens.push(Char('['));
|
||||
i += 1;
|
||||
}
|
||||
c => {
|
||||
tokens.push(Char(c));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pattern { tokens: tokens }
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape metacharacters within the given string by surrounding them in
|
||||
* brackets. The resulting string will, when compiled into a `Pattern`,
|
||||
* match the input string and nothing else.
|
||||
*/
|
||||
pub fn escape(s: &str) -> String {
|
||||
let mut escaped = String::new();
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
// note that ! does not need escaping because it is only special inside brackets
|
||||
'?' | '*' | '[' | ']' => {
|
||||
escaped.push_char('[');
|
||||
escaped.push_char(c);
|
||||
escaped.push_char(']');
|
||||
}
|
||||
c => {
|
||||
escaped.push_char(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
escaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given `str` matches this `Pattern` using the default
|
||||
* match options (i.e. `MatchOptions::new()`).
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* ```rust
|
||||
* #![allow(deprecated)]
|
||||
* use glob::Pattern;
|
||||
*
|
||||
* assert!(Pattern::new("c?t").matches("cat"));
|
||||
* assert!(Pattern::new("k[!e]tteh").matches("kitteh"));
|
||||
* assert!(Pattern::new("d*g").matches("doog"));
|
||||
* ```
|
||||
*/
|
||||
pub fn matches(&self, str: &str) -> bool {
|
||||
self.matches_with(str, MatchOptions::new())
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given `Path`, when converted to a `str`, matches this `Pattern`
|
||||
* using the default match options (i.e. `MatchOptions::new()`).
|
||||
*/
|
||||
pub fn matches_path(&self, path: &Path) -> bool {
|
||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||
path.as_str().map_or(false, |s| {
|
||||
self.matches(s)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given `str` matches this `Pattern` using the specified match options.
|
||||
*/
|
||||
pub fn matches_with(&self, str: &str, options: MatchOptions) -> bool {
|
||||
self.matches_from(None, str, 0, options) == Match
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given `Path`, when converted to a `str`, matches this `Pattern`
|
||||
* using the specified match options.
|
||||
*/
|
||||
pub fn matches_path_with(&self, path: &Path, options: MatchOptions) -> bool {
|
||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||
path.as_str().map_or(false, |s| {
|
||||
self.matches_with(s, options)
|
||||
})
|
||||
}
|
||||
|
||||
fn matches_from(&self,
|
||||
prev_char: Option<char>,
|
||||
mut file: &str,
|
||||
i: uint,
|
||||
options: MatchOptions) -> MatchResult {
|
||||
|
||||
let prev_char = Cell::new(prev_char);
|
||||
|
||||
let require_literal = |c| {
|
||||
(options.require_literal_separator && is_sep(c)) ||
|
||||
(options.require_literal_leading_dot && c == '.'
|
||||
&& is_sep(prev_char.get().unwrap_or('/')))
|
||||
};
|
||||
|
||||
for (ti, token) in self.tokens.slice_from(i).iter().enumerate() {
|
||||
match *token {
|
||||
AnySequence => {
|
||||
loop {
|
||||
match self.matches_from(prev_char.get(), file, i + ti + 1, options) {
|
||||
SubPatternDoesntMatch => (), // keep trying
|
||||
m => return m,
|
||||
}
|
||||
|
||||
if file.is_empty() {
|
||||
return EntirePatternDoesntMatch;
|
||||
}
|
||||
|
||||
let (some_c, next) = file.slice_shift_char();
|
||||
if require_literal(some_c.unwrap()) {
|
||||
return SubPatternDoesntMatch;
|
||||
}
|
||||
prev_char.set(some_c);
|
||||
file = next;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if file.is_empty() {
|
||||
return EntirePatternDoesntMatch;
|
||||
}
|
||||
|
||||
let (some_c, next) = file.slice_shift_char();
|
||||
let c = some_c.unwrap();
|
||||
let matches = match *token {
|
||||
AnyChar => {
|
||||
!require_literal(c)
|
||||
}
|
||||
AnyWithin(ref specifiers) => {
|
||||
!require_literal(c) &&
|
||||
in_char_specifiers(specifiers.as_slice(),
|
||||
c,
|
||||
options)
|
||||
}
|
||||
AnyExcept(ref specifiers) => {
|
||||
!require_literal(c) &&
|
||||
!in_char_specifiers(specifiers.as_slice(),
|
||||
c,
|
||||
options)
|
||||
}
|
||||
Char(c2) => {
|
||||
chars_eq(c, c2, options.case_sensitive)
|
||||
}
|
||||
AnySequence => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
if !matches {
|
||||
return SubPatternDoesntMatch;
|
||||
}
|
||||
prev_char.set(some_c);
|
||||
file = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if file.is_empty() {
|
||||
Match
|
||||
} else {
|
||||
SubPatternDoesntMatch
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Fills `todo` with paths under `path` to be matched by `patterns[idx]`,
|
||||
// special-casing patterns to match `.` and `..`, and avoiding `readdir()`
|
||||
// calls when there are no metacharacters in the pattern.
|
||||
fn fill_todo(todo: &mut Vec<(Path, uint)>, patterns: &[Pattern], idx: uint, path: &Path,
|
||||
options: MatchOptions) {
|
||||
// convert a pattern that's just many Char(_) to a string
|
||||
fn pattern_as_str(pattern: &Pattern) -> Option<String> {
|
||||
let mut s = String::new();
|
||||
for token in pattern.tokens.iter() {
|
||||
match *token {
|
||||
Char(c) => s.push_char(c),
|
||||
_ => return None
|
||||
}
|
||||
}
|
||||
return Some(s);
|
||||
}
|
||||
|
||||
let add = |todo: &mut Vec<_>, next_path: Path| {
|
||||
if idx + 1 == patterns.len() {
|
||||
// We know it's good, so don't make the iterator match this path
|
||||
// against the pattern again. In particular, it can't match
|
||||
// . or .. globs since these never show up as path components.
|
||||
todo.push((next_path, -1 as uint));
|
||||
} else {
|
||||
fill_todo(todo, patterns, idx + 1, &next_path, options);
|
||||
}
|
||||
};
|
||||
|
||||
let pattern = &patterns[idx];
|
||||
|
||||
match pattern_as_str(pattern) {
|
||||
Some(s) => {
|
||||
// This pattern component doesn't have any metacharacters, so we
|
||||
// don't need to read the current directory to know where to
|
||||
// continue. So instead of passing control back to the iterator,
|
||||
// we can just check for that one entry and potentially recurse
|
||||
// right away.
|
||||
let special = "." == s.as_slice() || ".." == s.as_slice();
|
||||
let next_path = path.join(s.as_slice());
|
||||
if (special && path.is_dir()) || (!special && next_path.exists()) {
|
||||
add(todo, next_path);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
match list_dir_sorted(path) {
|
||||
Some(entries) => {
|
||||
todo.extend(entries.into_iter().map(|x|(x, idx)));
|
||||
|
||||
// Matching the special directory entries . and .. that refer to
|
||||
// the current and parent directory respectively requires that
|
||||
// the pattern has a leading dot, even if the `MatchOptions` field
|
||||
// `require_literal_leading_dot` is not set.
|
||||
if pattern.tokens.len() > 0 && pattern.tokens[0] == Char('.') {
|
||||
for &special in [".", ".."].iter() {
|
||||
if pattern.matches_with(special, options) {
|
||||
add(todo, path.join(special));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_char_specifiers(s: &[char]) -> Vec<CharSpecifier> {
|
||||
let mut cs = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < s.len() {
|
||||
if i + 3 <= s.len() && s[i + 1] == '-' {
|
||||
cs.push(CharRange(s[i], s[i + 2]));
|
||||
i += 3;
|
||||
} else {
|
||||
cs.push(SingleChar(s[i]));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
cs
|
||||
}
|
||||
|
||||
fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptions) -> bool {
|
||||
|
||||
for &specifier in specifiers.iter() {
|
||||
match specifier {
|
||||
SingleChar(sc) => {
|
||||
if chars_eq(c, sc, options.case_sensitive) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CharRange(start, end) => {
|
||||
|
||||
// FIXME: work with non-ascii chars properly (issue #1347)
|
||||
if !options.case_sensitive && c.is_ascii() && start.is_ascii() && end.is_ascii() {
|
||||
|
||||
let start = start.to_ascii().to_lowercase();
|
||||
let end = end.to_ascii().to_lowercase();
|
||||
|
||||
let start_up = start.to_uppercase();
|
||||
let end_up = end.to_uppercase();
|
||||
|
||||
// only allow case insensitive matching when
|
||||
// both start and end are within a-z or A-Z
|
||||
if start != start_up && end != end_up {
|
||||
let start = start.to_char();
|
||||
let end = end.to_char();
|
||||
let c = c.to_ascii().to_lowercase().to_char();
|
||||
if c >= start && c <= end {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c >= start && c <= end {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// A helper function to determine if two chars are (possibly case-insensitively) equal.
|
||||
fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool {
|
||||
if cfg!(windows) && path::windows::is_sep(a) && path::windows::is_sep(b) {
|
||||
true
|
||||
} else if !case_sensitive && a.is_ascii() && b.is_ascii() {
|
||||
// FIXME: work with non-ascii chars properly (issue #1347)
|
||||
a.to_ascii().eq_ignore_case(b.to_ascii())
|
||||
} else {
|
||||
a == b
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options to modify the behaviour of `Pattern::matches_with(..)`
|
||||
*/
|
||||
#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct MatchOptions {
|
||||
|
||||
/**
|
||||
* Whether or not patterns should be matched in a case-sensitive manner. This
|
||||
* currently only considers upper/lower case relationships between ASCII characters,
|
||||
* but in future this might be extended to work with Unicode.
|
||||
*/
|
||||
pub case_sensitive: bool,
|
||||
|
||||
/**
|
||||
* If this is true then path-component separator characters (e.g. `/` on Posix)
|
||||
* must be matched by a literal `/`, rather than by `*` or `?` or `[...]`
|
||||
*/
|
||||
pub require_literal_separator: bool,
|
||||
|
||||
/**
|
||||
* If this is true then paths that contain components that start with a `.` will
|
||||
* not match unless the `.` appears literally in the pattern: `*`, `?` or `[...]`
|
||||
* will not match. This is useful because such files are conventionally considered
|
||||
* hidden on Unix systems and it might be desirable to skip them when listing files.
|
||||
*/
|
||||
pub require_literal_leading_dot: bool
|
||||
}
|
||||
|
||||
impl MatchOptions {
|
||||
|
||||
/**
|
||||
* Constructs a new `MatchOptions` with default field values. This is used
|
||||
* when calling functions that do not take an explicit `MatchOptions` parameter.
|
||||
*
|
||||
* This function always returns this value:
|
||||
*
|
||||
* ```rust,ignore
|
||||
* MatchOptions {
|
||||
* case_sensitive: true,
|
||||
* require_literal_separator: false.
|
||||
* require_literal_leading_dot: false
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
pub fn new() -> MatchOptions {
|
||||
MatchOptions {
|
||||
case_sensitive: true,
|
||||
require_literal_separator: false,
|
||||
require_literal_leading_dot: false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::os;
|
||||
use super::{glob, Pattern, MatchOptions};
|
||||
|
||||
#[test]
|
||||
fn test_absolute_pattern() {
|
||||
// assume that the filesystem is not empty!
|
||||
assert!(glob("/*").next().is_some());
|
||||
assert!(glob("//").next().is_some());
|
||||
|
||||
// check windows absolute paths with host/device components
|
||||
let root_with_device = os::getcwd().root_path().unwrap().join("*");
|
||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||
assert!(glob(root_with_device.as_str().unwrap()).next().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wildcard_optimizations() {
|
||||
assert!(Pattern::new("a*b").matches("a___b"));
|
||||
assert!(Pattern::new("a**b").matches("a___b"));
|
||||
assert!(Pattern::new("a***b").matches("a___b"));
|
||||
assert!(Pattern::new("a*b*c").matches("abc"));
|
||||
assert!(!Pattern::new("a*b*c").matches("abcd"));
|
||||
assert!(Pattern::new("a*b*c").matches("a_b_c"));
|
||||
assert!(Pattern::new("a*b*c").matches("a___b___c"));
|
||||
assert!(Pattern::new("abc*abc*abc").matches("abcabcabcabcabcabcabc"));
|
||||
assert!(!Pattern::new("abc*abc*abc").matches("abcabcabcabcabcabcabca"));
|
||||
assert!(Pattern::new("a*a*a*a*a*a*a*a*a").matches("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||
assert!(Pattern::new("a*b[xyz]c*d").matches("abxcdbxcddd"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore)] // FIXME (#9406)
|
||||
fn test_lots_of_files() {
|
||||
// this is a good test because it touches lots of differently named files
|
||||
glob("/*/*/*/*").skip(10000).next();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_pattern() {
|
||||
|
||||
let pat = Pattern::new("a[0-9]b");
|
||||
for i in range(0u, 10) {
|
||||
assert!(pat.matches(format!("a{}b", i).as_slice()));
|
||||
}
|
||||
assert!(!pat.matches("a_b"));
|
||||
|
||||
let pat = Pattern::new("a[!0-9]b");
|
||||
for i in range(0u, 10) {
|
||||
assert!(!pat.matches(format!("a{}b", i).as_slice()));
|
||||
}
|
||||
assert!(pat.matches("a_b"));
|
||||
|
||||
let pats = ["[a-z123]", "[1a-z23]", "[123a-z]"];
|
||||
for &p in pats.iter() {
|
||||
let pat = Pattern::new(p);
|
||||
for c in "abcdefghijklmnopqrstuvwxyz".chars() {
|
||||
assert!(pat.matches(c.to_string().as_slice()));
|
||||
}
|
||||
for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars() {
|
||||
let options = MatchOptions {case_sensitive: false, .. MatchOptions::new()};
|
||||
assert!(pat.matches_with(c.to_string().as_slice(), options));
|
||||
}
|
||||
assert!(pat.matches("1"));
|
||||
assert!(pat.matches("2"));
|
||||
assert!(pat.matches("3"));
|
||||
}
|
||||
|
||||
let pats = ["[abc-]", "[-abc]", "[a-c-]"];
|
||||
for &p in pats.iter() {
|
||||
let pat = Pattern::new(p);
|
||||
assert!(pat.matches("a"));
|
||||
assert!(pat.matches("b"));
|
||||
assert!(pat.matches("c"));
|
||||
assert!(pat.matches("-"));
|
||||
assert!(!pat.matches("d"));
|
||||
}
|
||||
|
||||
let pat = Pattern::new("[2-1]");
|
||||
assert!(!pat.matches("1"));
|
||||
assert!(!pat.matches("2"));
|
||||
|
||||
assert!(Pattern::new("[-]").matches("-"));
|
||||
assert!(!Pattern::new("[!-]").matches("-"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unclosed_bracket() {
|
||||
// unclosed `[` should be treated literally
|
||||
assert!(Pattern::new("abc[def").matches("abc[def"));
|
||||
assert!(Pattern::new("abc[!def").matches("abc[!def"));
|
||||
assert!(Pattern::new("abc[").matches("abc["));
|
||||
assert!(Pattern::new("abc[!").matches("abc[!"));
|
||||
assert!(Pattern::new("abc[d").matches("abc[d"));
|
||||
assert!(Pattern::new("abc[!d").matches("abc[!d"));
|
||||
assert!(Pattern::new("abc[]").matches("abc[]"));
|
||||
assert!(Pattern::new("abc[!]").matches("abc[!]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pattern_matches() {
|
||||
let txt_pat = Pattern::new("*hello.txt");
|
||||
assert!(txt_pat.matches("hello.txt"));
|
||||
assert!(txt_pat.matches("gareth_says_hello.txt"));
|
||||
assert!(txt_pat.matches("some/path/to/hello.txt"));
|
||||
assert!(txt_pat.matches("some\\path\\to\\hello.txt"));
|
||||
assert!(txt_pat.matches("/an/absolute/path/to/hello.txt"));
|
||||
assert!(!txt_pat.matches("hello.txt-and-then-some"));
|
||||
assert!(!txt_pat.matches("goodbye.txt"));
|
||||
|
||||
let dir_pat = Pattern::new("*some/path/to/hello.txt");
|
||||
assert!(dir_pat.matches("some/path/to/hello.txt"));
|
||||
assert!(dir_pat.matches("a/bigger/some/path/to/hello.txt"));
|
||||
assert!(!dir_pat.matches("some/path/to/hello.txt-and-then-some"));
|
||||
assert!(!dir_pat.matches("some/other/path/to/hello.txt"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pattern_escape() {
|
||||
let s = "_[_]_?_*_!_";
|
||||
assert_eq!(Pattern::escape(s), "_[[]_[]]_[?]_[*]_!_".to_string());
|
||||
assert!(Pattern::new(Pattern::escape(s).as_slice()).matches(s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pattern_matches_case_insensitive() {
|
||||
|
||||
let pat = Pattern::new("aBcDeFg");
|
||||
let options = MatchOptions {
|
||||
case_sensitive: false,
|
||||
require_literal_separator: false,
|
||||
require_literal_leading_dot: false
|
||||
};
|
||||
|
||||
assert!(pat.matches_with("aBcDeFg", options));
|
||||
assert!(pat.matches_with("abcdefg", options));
|
||||
assert!(pat.matches_with("ABCDEFG", options));
|
||||
assert!(pat.matches_with("AbCdEfG", options));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pattern_matches_case_insensitive_range() {
|
||||
|
||||
let pat_within = Pattern::new("[a]");
|
||||
let pat_except = Pattern::new("[!a]");
|
||||
|
||||
let options_case_insensitive = MatchOptions {
|
||||
case_sensitive: false,
|
||||
require_literal_separator: false,
|
||||
require_literal_leading_dot: false
|
||||
};
|
||||
let options_case_sensitive = MatchOptions {
|
||||
case_sensitive: true,
|
||||
require_literal_separator: false,
|
||||
require_literal_leading_dot: false
|
||||
};
|
||||
|
||||
assert!(pat_within.matches_with("a", options_case_insensitive));
|
||||
assert!(pat_within.matches_with("A", options_case_insensitive));
|
||||
assert!(!pat_within.matches_with("A", options_case_sensitive));
|
||||
|
||||
assert!(!pat_except.matches_with("a", options_case_insensitive));
|
||||
assert!(!pat_except.matches_with("A", options_case_insensitive));
|
||||
assert!(pat_except.matches_with("A", options_case_sensitive));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pattern_matches_require_literal_separator() {
|
||||
|
||||
let options_require_literal = MatchOptions {
|
||||
case_sensitive: true,
|
||||
require_literal_separator: true,
|
||||
require_literal_leading_dot: false
|
||||
};
|
||||
let options_not_require_literal = MatchOptions {
|
||||
case_sensitive: true,
|
||||
require_literal_separator: false,
|
||||
require_literal_leading_dot: false
|
||||
};
|
||||
|
||||
assert!(Pattern::new("abc/def").matches_with("abc/def", options_require_literal));
|
||||
assert!(!Pattern::new("abc?def").matches_with("abc/def", options_require_literal));
|
||||
assert!(!Pattern::new("abc*def").matches_with("abc/def", options_require_literal));
|
||||
assert!(!Pattern::new("abc[/]def").matches_with("abc/def", options_require_literal));
|
||||
|
||||
assert!(Pattern::new("abc/def").matches_with("abc/def", options_not_require_literal));
|
||||
assert!(Pattern::new("abc?def").matches_with("abc/def", options_not_require_literal));
|
||||
assert!(Pattern::new("abc*def").matches_with("abc/def", options_not_require_literal));
|
||||
assert!(Pattern::new("abc[/]def").matches_with("abc/def", options_not_require_literal));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pattern_matches_require_literal_leading_dot() {
|
||||
|
||||
let options_require_literal_leading_dot = MatchOptions {
|
||||
case_sensitive: true,
|
||||
require_literal_separator: false,
|
||||
require_literal_leading_dot: true
|
||||
};
|
||||
let options_not_require_literal_leading_dot = MatchOptions {
|
||||
case_sensitive: true,
|
||||
require_literal_separator: false,
|
||||
require_literal_leading_dot: false
|
||||
};
|
||||
|
||||
let f = |options| Pattern::new("*.txt").matches_with(".hello.txt", options);
|
||||
assert!(f(options_not_require_literal_leading_dot));
|
||||
assert!(!f(options_require_literal_leading_dot));
|
||||
|
||||
let f = |options| Pattern::new(".*.*").matches_with(".hello.txt", options);
|
||||
assert!(f(options_not_require_literal_leading_dot));
|
||||
assert!(f(options_require_literal_leading_dot));
|
||||
|
||||
let f = |options| Pattern::new("aaa/bbb/*").matches_with("aaa/bbb/.ccc", options);
|
||||
assert!(f(options_not_require_literal_leading_dot));
|
||||
assert!(!f(options_require_literal_leading_dot));
|
||||
|
||||
let f = |options| Pattern::new("aaa/bbb/*").matches_with("aaa/bbb/c.c.c.", options);
|
||||
assert!(f(options_not_require_literal_leading_dot));
|
||||
assert!(f(options_require_literal_leading_dot));
|
||||
|
||||
let f = |options| Pattern::new("aaa/bbb/.*").matches_with("aaa/bbb/.ccc", options);
|
||||
assert!(f(options_not_require_literal_leading_dot));
|
||||
assert!(f(options_require_literal_leading_dot));
|
||||
|
||||
let f = |options| Pattern::new("aaa/?bbb").matches_with("aaa/.bbb", options);
|
||||
assert!(f(options_not_require_literal_leading_dot));
|
||||
assert!(!f(options_require_literal_leading_dot));
|
||||
|
||||
let f = |options| Pattern::new("aaa/[.]bbb").matches_with("aaa/.bbb", options);
|
||||
assert!(f(options_not_require_literal_leading_dot));
|
||||
assert!(!f(options_require_literal_leading_dot));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matches_path() {
|
||||
// on windows, (Path::new("a/b").as_str().unwrap() == "a\\b"), so this
|
||||
// tests that / and \ are considered equivalent on windows
|
||||
assert!(Pattern::new("a/b").matches_path(&Path::new("a/b")));
|
||||
}
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
Syntax extension to create floating point literals from hexadecimal strings
|
||||
|
||||
Once loaded, hexfloat!() is called with a string containing the hexadecimal
|
||||
floating-point literal, and an optional type (f32 or f64).
|
||||
If the type is omitted, the literal is treated the same as a normal unsuffixed
|
||||
literal.
|
||||
|
||||
# Examples
|
||||
|
||||
To load the extension and use it:
|
||||
|
||||
```rust,ignore
|
||||
#[phase(plugin)]
|
||||
extern crate hexfloat;
|
||||
|
||||
fn main() {
|
||||
let val = hexfloat!("0x1.ffffb4", f32);
|
||||
}
|
||||
```
|
||||
|
||||
# References
|
||||
|
||||
* [ExploringBinary: hexadecimal floating point constants]
|
||||
(http://www.exploringbinary.com/hexadecimal-floating-point-constants/)
|
||||
|
||||
*/
|
||||
|
||||
#![crate_name = "hexfloat"]
|
||||
#![deprecated = "This is now a cargo package located at: \
|
||||
https://github.com/rust-lang/hexfloat"]
|
||||
#![allow(deprecated)]
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
#![license = "MIT/ASL2"]
|
||||
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||
html_root_url = "http://doc.rust-lang.org/nightly/")]
|
||||
#![feature(plugin_registrar)]
|
||||
|
||||
extern crate syntax;
|
||||
extern crate rustc;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::codemap::{Span, mk_sp};
|
||||
use syntax::ext::base;
|
||||
use syntax::ext::base::{ExtCtxt, MacExpr};
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::parse::token;
|
||||
use syntax::ptr::P;
|
||||
use rustc::plugin::Registry;
|
||||
|
||||
#[plugin_registrar]
|
||||
pub fn plugin_registrar(reg: &mut Registry) {
|
||||
reg.register_macro("hexfloat", expand_syntax_ext);
|
||||
}
|
||||
|
||||
//Check if the literal is valid (as LLVM expects),
|
||||
//and return a descriptive error if not.
|
||||
fn hex_float_lit_err(s: &str) -> Option<(uint, String)> {
|
||||
let mut chars = s.chars().peekable();
|
||||
let mut i = 0;
|
||||
if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
|
||||
if chars.next() != Some('0') {
|
||||
return Some((i, "Expected '0'".to_string()));
|
||||
} i+=1;
|
||||
if chars.next() != Some('x') {
|
||||
return Some((i, "Expected 'x'".to_string()));
|
||||
} i+=1;
|
||||
let mut d_len = 0i;
|
||||
for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; d_len += 1;}
|
||||
if chars.next() != Some('.') {
|
||||
return Some((i, "Expected '.'".to_string()));
|
||||
} i+=1;
|
||||
let mut f_len = 0i;
|
||||
for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; f_len += 1;}
|
||||
if d_len == 0 && f_len == 0 {
|
||||
return Some((i, "Expected digits before or after decimal \
|
||||
point".to_string()));
|
||||
}
|
||||
if chars.next() != Some('p') {
|
||||
return Some((i, "Expected 'p'".to_string()));
|
||||
} i+=1;
|
||||
if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
|
||||
let mut e_len = 0i;
|
||||
for _ in chars.take_while(|c| c.is_digit()) { chars.next(); i+=1; e_len += 1}
|
||||
if e_len == 0 {
|
||||
return Some((i, "Expected exponent digits".to_string()));
|
||||
}
|
||||
match chars.next() {
|
||||
None => None,
|
||||
Some(_) => Some((i, "Expected end of string".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
-> Box<base::MacResult+'static> {
|
||||
let (expr, ty_lit) = parse_tts(cx, tts);
|
||||
|
||||
let ty = match ty_lit {
|
||||
None => None,
|
||||
Some(Ident{ident, span}) => match token::get_ident(ident).get() {
|
||||
"f32" => Some(ast::TyF32),
|
||||
"f64" => Some(ast::TyF64),
|
||||
_ => {
|
||||
cx.span_err(span, "invalid floating point type in hexfloat!");
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let s = match expr.node {
|
||||
// expression is a literal
|
||||
ast::ExprLit(ref lit) => match lit.node {
|
||||
// string literal
|
||||
ast::LitStr(ref s, _) => {
|
||||
s.clone()
|
||||
}
|
||||
_ => {
|
||||
cx.span_err(expr.span, "unsupported literal in hexfloat!");
|
||||
return base::DummyResult::expr(sp);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
cx.span_err(expr.span, "non-literal in hexfloat!");
|
||||
return base::DummyResult::expr(sp);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
let err = hex_float_lit_err(s.get());
|
||||
match err {
|
||||
Some((err_pos, err_str)) => {
|
||||
let pos = expr.span.lo + syntax::codemap::Pos::from_uint(err_pos + 1);
|
||||
let span = syntax::codemap::mk_sp(pos,pos);
|
||||
cx.span_err(span,
|
||||
format!("invalid hex float literal in hexfloat!: \
|
||||
{}",
|
||||
err_str).as_slice());
|
||||
return base::DummyResult::expr(sp);
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
let lit = match ty {
|
||||
None => ast::LitFloatUnsuffixed(s),
|
||||
Some (ty) => ast::LitFloat(s, ty)
|
||||
};
|
||||
MacExpr::new(cx.expr_lit(sp, lit))
|
||||
}
|
||||
|
||||
struct Ident {
|
||||
ident: ast::Ident,
|
||||
span: Span
|
||||
}
|
||||
|
||||
fn parse_tts(cx: &ExtCtxt,
|
||||
tts: &[ast::TokenTree]) -> (P<ast::Expr>, Option<Ident>) {
|
||||
let p = &mut cx.new_parser_from_tts(tts);
|
||||
let ex = p.parse_expr();
|
||||
let id = if p.token == token::EOF {
|
||||
None
|
||||
} else {
|
||||
p.expect(&token::COMMA);
|
||||
let lo = p.span.lo;
|
||||
let ident = p.parse_ident();
|
||||
let hi = p.last_span.hi;
|
||||
Some(Ident{ident: ident, span: mk_sp(lo, hi)})
|
||||
};
|
||||
if p.token != token::EOF {
|
||||
p.unexpected();
|
||||
}
|
||||
(ex, id)
|
||||
}
|
||||
|
||||
// FIXME (10872): This is required to prevent an LLVM assert on Windows
|
||||
#[test]
|
||||
fn dummy_test() { }
|
2961
src/libnum/bigint.rs
2961
src/libnum/bigint.rs
File diff suppressed because it is too large
Load Diff
@ -1,379 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
//! Complex numbers.
|
||||
|
||||
use std::fmt;
|
||||
use std::num::{Zero, One, ToStrRadix};
|
||||
|
||||
// FIXME #1284: handle complex NaN & infinity etc. This
|
||||
// probably doesn't map to C's _Complex correctly.
|
||||
|
||||
/// A complex number in Cartesian form.
|
||||
#[deriving(PartialEq, Clone, Hash)]
|
||||
pub struct Complex<T> {
|
||||
/// Real portion of the complex number
|
||||
pub re: T,
|
||||
/// Imaginary portion of the complex number
|
||||
pub im: T
|
||||
}
|
||||
|
||||
pub type Complex32 = Complex<f32>;
|
||||
pub type Complex64 = Complex<f64>;
|
||||
|
||||
impl<T: Clone + Num> Complex<T> {
|
||||
/// Create a new Complex
|
||||
#[inline]
|
||||
pub fn new(re: T, im: T) -> Complex<T> {
|
||||
Complex { re: re, im: im }
|
||||
}
|
||||
|
||||
/// Returns the square of the norm (since `T` doesn't necessarily
|
||||
/// have a sqrt function), i.e. `re^2 + im^2`.
|
||||
#[inline]
|
||||
pub fn norm_sqr(&self) -> T {
|
||||
self.re * self.re + self.im * self.im
|
||||
}
|
||||
|
||||
|
||||
/// Returns the complex conjugate. i.e. `re - i im`
|
||||
#[inline]
|
||||
pub fn conj(&self) -> Complex<T> {
|
||||
Complex::new(self.re.clone(), -self.im)
|
||||
}
|
||||
|
||||
|
||||
/// Multiplies `self` by the scalar `t`.
|
||||
#[inline]
|
||||
pub fn scale(&self, t: T) -> Complex<T> {
|
||||
Complex::new(self.re * t, self.im * t)
|
||||
}
|
||||
|
||||
/// Divides `self` by the scalar `t`.
|
||||
#[inline]
|
||||
pub fn unscale(&self, t: T) -> Complex<T> {
|
||||
Complex::new(self.re / t, self.im / t)
|
||||
}
|
||||
|
||||
/// Returns `1/self`
|
||||
#[inline]
|
||||
pub fn inv(&self) -> Complex<T> {
|
||||
let norm_sqr = self.norm_sqr();
|
||||
Complex::new(self.re / norm_sqr,
|
||||
-self.im / norm_sqr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + FloatMath> Complex<T> {
|
||||
/// Calculate |self|
|
||||
#[inline]
|
||||
pub fn norm(&self) -> T {
|
||||
self.re.hypot(self.im)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + FloatMath> Complex<T> {
|
||||
/// Calculate the principal Arg of self.
|
||||
#[inline]
|
||||
pub fn arg(&self) -> T {
|
||||
self.im.atan2(self.re)
|
||||
}
|
||||
/// Convert to polar form (r, theta), such that `self = r * exp(i
|
||||
/// * theta)`
|
||||
#[inline]
|
||||
pub fn to_polar(&self) -> (T, T) {
|
||||
(self.norm(), self.arg())
|
||||
}
|
||||
/// Convert a polar representation into a complex number.
|
||||
#[inline]
|
||||
pub fn from_polar(r: &T, theta: &T) -> Complex<T> {
|
||||
Complex::new(*r * theta.cos(), *r * theta.sin())
|
||||
}
|
||||
}
|
||||
|
||||
/* arithmetic */
|
||||
// (a + i b) + (c + i d) == (a + c) + i (b + d)
|
||||
impl<T: Clone + Num> Add<Complex<T>, Complex<T>> for Complex<T> {
|
||||
#[inline]
|
||||
fn add(&self, other: &Complex<T>) -> Complex<T> {
|
||||
Complex::new(self.re + other.re, self.im + other.im)
|
||||
}
|
||||
}
|
||||
// (a + i b) - (c + i d) == (a - c) + i (b - d)
|
||||
impl<T: Clone + Num> Sub<Complex<T>, Complex<T>> for Complex<T> {
|
||||
#[inline]
|
||||
fn sub(&self, other: &Complex<T>) -> Complex<T> {
|
||||
Complex::new(self.re - other.re, self.im - other.im)
|
||||
}
|
||||
}
|
||||
// (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c)
|
||||
impl<T: Clone + Num> Mul<Complex<T>, Complex<T>> for Complex<T> {
|
||||
#[inline]
|
||||
fn mul(&self, other: &Complex<T>) -> Complex<T> {
|
||||
Complex::new(self.re*other.re - self.im*other.im,
|
||||
self.re*other.im + self.im*other.re)
|
||||
}
|
||||
}
|
||||
|
||||
// (a + i b) / (c + i d) == [(a + i b) * (c - i d)] / (c*c + d*d)
|
||||
// == [(a*c + b*d) / (c*c + d*d)] + i [(b*c - a*d) / (c*c + d*d)]
|
||||
impl<T: Clone + Num> Div<Complex<T>, Complex<T>> for Complex<T> {
|
||||
#[inline]
|
||||
fn div(&self, other: &Complex<T>) -> Complex<T> {
|
||||
let norm_sqr = other.norm_sqr();
|
||||
Complex::new((self.re*other.re + self.im*other.im) / norm_sqr,
|
||||
(self.im*other.re - self.re*other.im) / norm_sqr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Num> Neg<Complex<T>> for Complex<T> {
|
||||
#[inline]
|
||||
fn neg(&self) -> Complex<T> {
|
||||
Complex::new(-self.re, -self.im)
|
||||
}
|
||||
}
|
||||
|
||||
/* constants */
|
||||
impl<T: Clone + Num> Zero for Complex<T> {
|
||||
#[inline]
|
||||
fn zero() -> Complex<T> {
|
||||
Complex::new(Zero::zero(), Zero::zero())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_zero(&self) -> bool {
|
||||
self.re.is_zero() && self.im.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Num> One for Complex<T> {
|
||||
#[inline]
|
||||
fn one() -> Complex<T> {
|
||||
Complex::new(One::one(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
/* string conversions */
|
||||
impl<T: fmt::Show + Num + PartialOrd> fmt::Show for Complex<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.im < Zero::zero() {
|
||||
write!(f, "{}-{}i", self.re, -self.im)
|
||||
} else {
|
||||
write!(f, "{}+{}i", self.re, self.im)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToStrRadix + Num + PartialOrd> ToStrRadix for Complex<T> {
|
||||
fn to_str_radix(&self, radix: uint) -> String {
|
||||
if self.im < Zero::zero() {
|
||||
format!("{}-{}i",
|
||||
self.re.to_str_radix(radix),
|
||||
(-self.im).to_str_radix(radix))
|
||||
} else {
|
||||
format!("{}+{}i",
|
||||
self.re.to_str_radix(radix),
|
||||
self.im.to_str_radix(radix))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#![allow(non_uppercase_statics)]
|
||||
|
||||
use super::{Complex64, Complex};
|
||||
use std::num::{Zero, One, Float};
|
||||
use std::hash::hash;
|
||||
|
||||
pub const _0_0i : Complex64 = Complex { re: 0.0, im: 0.0 };
|
||||
pub const _1_0i : Complex64 = Complex { re: 1.0, im: 0.0 };
|
||||
pub const _1_1i : Complex64 = Complex { re: 1.0, im: 1.0 };
|
||||
pub const _0_1i : Complex64 = Complex { re: 0.0, im: 1.0 };
|
||||
pub const _neg1_1i : Complex64 = Complex { re: -1.0, im: 1.0 };
|
||||
pub const _05_05i : Complex64 = Complex { re: 0.5, im: 0.5 };
|
||||
pub const all_consts : [Complex64, .. 5] = [_0_0i, _1_0i, _1_1i, _neg1_1i, _05_05i];
|
||||
|
||||
#[test]
|
||||
fn test_consts() {
|
||||
// check our constants are what Complex::new creates
|
||||
fn test(c : Complex64, r : f64, i: f64) {
|
||||
assert_eq!(c, Complex::new(r,i));
|
||||
}
|
||||
test(_0_0i, 0.0, 0.0);
|
||||
test(_1_0i, 1.0, 0.0);
|
||||
test(_1_1i, 1.0, 1.0);
|
||||
test(_neg1_1i, -1.0, 1.0);
|
||||
test(_05_05i, 0.5, 0.5);
|
||||
|
||||
assert_eq!(_0_0i, Zero::zero());
|
||||
assert_eq!(_1_0i, One::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "x86", ignore)]
|
||||
// FIXME #7158: (maybe?) currently failing on x86.
|
||||
fn test_norm() {
|
||||
fn test(c: Complex64, ns: f64) {
|
||||
assert_eq!(c.norm_sqr(), ns);
|
||||
assert_eq!(c.norm(), ns.sqrt())
|
||||
}
|
||||
test(_0_0i, 0.0);
|
||||
test(_1_0i, 1.0);
|
||||
test(_1_1i, 2.0);
|
||||
test(_neg1_1i, 2.0);
|
||||
test(_05_05i, 0.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scale_unscale() {
|
||||
assert_eq!(_05_05i.scale(2.0), _1_1i);
|
||||
assert_eq!(_1_1i.unscale(2.0), _05_05i);
|
||||
for &c in all_consts.iter() {
|
||||
assert_eq!(c.scale(2.0).unscale(2.0), c);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conj() {
|
||||
for &c in all_consts.iter() {
|
||||
assert_eq!(c.conj(), Complex::new(c.re, -c.im));
|
||||
assert_eq!(c.conj().conj(), c);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inv() {
|
||||
assert_eq!(_1_1i.inv(), _05_05i.conj());
|
||||
assert_eq!(_1_0i.inv(), _1_0i.inv());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_divide_by_zero_natural() {
|
||||
let n = Complex::new(2i, 3i);
|
||||
let d = Complex::new(0, 0);
|
||||
let _x = n / d;
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
#[ignore]
|
||||
fn test_inv_zero() {
|
||||
// FIXME #5736: should this really fail, or just NaN?
|
||||
_0_0i.inv();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arg() {
|
||||
fn test(c: Complex64, arg: f64) {
|
||||
assert!((c.arg() - arg).abs() < 1.0e-6)
|
||||
}
|
||||
test(_1_0i, 0.0);
|
||||
test(_1_1i, 0.25 * Float::pi());
|
||||
test(_neg1_1i, 0.75 * Float::pi());
|
||||
test(_05_05i, 0.25 * Float::pi());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_polar_conv() {
|
||||
fn test(c: Complex64) {
|
||||
let (r, theta) = c.to_polar();
|
||||
assert!((c - Complex::from_polar(&r, &theta)).norm() < 1e-6);
|
||||
}
|
||||
for &c in all_consts.iter() { test(c); }
|
||||
}
|
||||
|
||||
mod arith {
|
||||
use super::{_0_0i, _1_0i, _1_1i, _0_1i, _neg1_1i, _05_05i, all_consts};
|
||||
use std::num::Zero;
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
assert_eq!(_05_05i + _05_05i, _1_1i);
|
||||
assert_eq!(_0_1i + _1_0i, _1_1i);
|
||||
assert_eq!(_1_0i + _neg1_1i, _0_1i);
|
||||
|
||||
for &c in all_consts.iter() {
|
||||
assert_eq!(_0_0i + c, c);
|
||||
assert_eq!(c + _0_0i, c);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
assert_eq!(_05_05i - _05_05i, _0_0i);
|
||||
assert_eq!(_0_1i - _1_0i, _neg1_1i);
|
||||
assert_eq!(_0_1i - _neg1_1i, _1_0i);
|
||||
|
||||
for &c in all_consts.iter() {
|
||||
assert_eq!(c - _0_0i, c);
|
||||
assert_eq!(c - c, _0_0i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
assert_eq!(_05_05i * _05_05i, _0_1i.unscale(2.0));
|
||||
assert_eq!(_1_1i * _0_1i, _neg1_1i);
|
||||
|
||||
// i^2 & i^4
|
||||
assert_eq!(_0_1i * _0_1i, -_1_0i);
|
||||
assert_eq!(_0_1i * _0_1i * _0_1i * _0_1i, _1_0i);
|
||||
|
||||
for &c in all_consts.iter() {
|
||||
assert_eq!(c * _1_0i, c);
|
||||
assert_eq!(_1_0i * c, c);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_div() {
|
||||
assert_eq!(_neg1_1i / _0_1i, _1_1i);
|
||||
for &c in all_consts.iter() {
|
||||
if c != Zero::zero() {
|
||||
assert_eq!(c / c, _1_0i);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_neg() {
|
||||
assert_eq!(-_1_0i + _0_1i, _neg1_1i);
|
||||
assert_eq!((-_0_1i) * _0_1i, _1_0i);
|
||||
for &c in all_consts.iter() {
|
||||
assert_eq!(-(-c), c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_string() {
|
||||
fn test(c : Complex64, s: String) {
|
||||
assert_eq!(c.to_string(), s);
|
||||
}
|
||||
test(_0_0i, "0+0i".to_string());
|
||||
test(_1_0i, "1+0i".to_string());
|
||||
test(_0_1i, "0+1i".to_string());
|
||||
test(_1_1i, "1+1i".to_string());
|
||||
test(_neg1_1i, "-1+1i".to_string());
|
||||
test(-_neg1_1i, "1-1i".to_string());
|
||||
test(_05_05i, "0.5+0.5i".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash() {
|
||||
let a = Complex::new(0i32, 0i32);
|
||||
let b = Complex::new(1i32, 0i32);
|
||||
let c = Complex::new(0i32, 1i32);
|
||||
assert!(hash(&a) != hash(&b));
|
||||
assert!(hash(&b) != hash(&c));
|
||||
assert!(hash(&c) != hash(&a));
|
||||
}
|
||||
}
|
@ -1,507 +0,0 @@
|
||||
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Integer trait and functions.
|
||||
|
||||
pub trait Integer: Num + PartialOrd
|
||||
+ Div<Self, Self>
|
||||
+ Rem<Self, Self> {
|
||||
/// Floored integer division.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// assert!(( 8i).div_floor(& 3) == 2);
|
||||
/// assert!(( 8i).div_floor(&-3) == -3);
|
||||
/// assert!((-8i).div_floor(& 3) == -3);
|
||||
/// assert!((-8i).div_floor(&-3) == 2);
|
||||
///
|
||||
/// assert!(( 1i).div_floor(& 2) == 0);
|
||||
/// assert!(( 1i).div_floor(&-2) == -1);
|
||||
/// assert!((-1i).div_floor(& 2) == -1);
|
||||
/// assert!((-1i).div_floor(&-2) == 0);
|
||||
/// ```
|
||||
fn div_floor(&self, other: &Self) -> Self;
|
||||
|
||||
/// Floored integer modulo, satisfying:
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// # let n = 1i; let d = 1i;
|
||||
/// assert!(n.div_floor(&d) * d + n.mod_floor(&d) == n)
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// assert!(( 8i).mod_floor(& 3) == 2);
|
||||
/// assert!(( 8i).mod_floor(&-3) == -1);
|
||||
/// assert!((-8i).mod_floor(& 3) == 1);
|
||||
/// assert!((-8i).mod_floor(&-3) == -2);
|
||||
///
|
||||
/// assert!(( 1i).mod_floor(& 2) == 1);
|
||||
/// assert!(( 1i).mod_floor(&-2) == -1);
|
||||
/// assert!((-1i).mod_floor(& 2) == 1);
|
||||
/// assert!((-1i).mod_floor(&-2) == -1);
|
||||
/// ```
|
||||
fn mod_floor(&self, other: &Self) -> Self;
|
||||
|
||||
/// Greatest Common Divisor (GCD).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// assert_eq!(6i.gcd(&8), 2);
|
||||
/// assert_eq!(7i.gcd(&3), 1);
|
||||
/// ```
|
||||
fn gcd(&self, other: &Self) -> Self;
|
||||
|
||||
/// Lowest Common Multiple (LCM).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// assert_eq!(7i.lcm(&3), 21);
|
||||
/// assert_eq!(2i.lcm(&4), 4);
|
||||
/// ```
|
||||
fn lcm(&self, other: &Self) -> Self;
|
||||
|
||||
/// Deprecated, use `is_multiple_of` instead.
|
||||
#[deprecated = "function renamed to `is_multiple_of`"]
|
||||
fn divides(&self, other: &Self) -> bool;
|
||||
|
||||
/// Returns `true` if `other` is a multiple of `self`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// assert_eq!(9i.is_multiple_of(&3), true);
|
||||
/// assert_eq!(3i.is_multiple_of(&9), false);
|
||||
/// ```
|
||||
fn is_multiple_of(&self, other: &Self) -> bool;
|
||||
|
||||
/// Returns `true` if the number is even.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// assert_eq!(3i.is_even(), false);
|
||||
/// assert_eq!(4i.is_even(), true);
|
||||
/// ```
|
||||
fn is_even(&self) -> bool;
|
||||
|
||||
/// Returns `true` if the number is odd.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// assert_eq!(3i.is_odd(), true);
|
||||
/// assert_eq!(4i.is_odd(), false);
|
||||
/// ```
|
||||
fn is_odd(&self) -> bool;
|
||||
|
||||
/// Simultaneous truncated integer division and modulus.
|
||||
/// Returns `(quotient, remainder)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// assert_eq!(( 8i).div_rem( &3), ( 2, 2));
|
||||
/// assert_eq!(( 8i).div_rem(&-3), (-2, 2));
|
||||
/// assert_eq!((-8i).div_rem( &3), (-2, -2));
|
||||
/// assert_eq!((-8i).div_rem(&-3), ( 2, -2));
|
||||
///
|
||||
/// assert_eq!(( 1i).div_rem( &2), ( 0, 1));
|
||||
/// assert_eq!(( 1i).div_rem(&-2), ( 0, 1));
|
||||
/// assert_eq!((-1i).div_rem( &2), ( 0, -1));
|
||||
/// assert_eq!((-1i).div_rem(&-2), ( 0, -1));
|
||||
/// ```
|
||||
#[inline]
|
||||
fn div_rem(&self, other: &Self) -> (Self, Self) {
|
||||
(*self / *other, *self % *other)
|
||||
}
|
||||
|
||||
/// Simultaneous floored integer division and modulus.
|
||||
/// Returns `(quotient, remainder)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![allow(deprecated)]
|
||||
/// # use num::Integer;
|
||||
/// assert_eq!(( 8i).div_mod_floor( &3), ( 2, 2));
|
||||
/// assert_eq!(( 8i).div_mod_floor(&-3), (-3, -1));
|
||||
/// assert_eq!((-8i).div_mod_floor( &3), (-3, 1));
|
||||
/// assert_eq!((-8i).div_mod_floor(&-3), ( 2, -2));
|
||||
///
|
||||
/// assert_eq!(( 1i).div_mod_floor( &2), ( 0, 1));
|
||||
/// assert_eq!(( 1i).div_mod_floor(&-2), (-1, -1));
|
||||
/// assert_eq!((-1i).div_mod_floor( &2), (-1, 1));
|
||||
/// assert_eq!((-1i).div_mod_floor(&-2), ( 0, -1));
|
||||
/// ```
|
||||
fn div_mod_floor(&self, other: &Self) -> (Self, Self) {
|
||||
(self.div_floor(other), self.mod_floor(other))
|
||||
}
|
||||
}
|
||||
|
||||
/// Simultaneous integer division and modulus
|
||||
#[inline] pub fn div_rem<T: Integer>(x: T, y: T) -> (T, T) { x.div_rem(&y) }
|
||||
/// Floored integer division
|
||||
#[inline] pub fn div_floor<T: Integer>(x: T, y: T) -> T { x.div_floor(&y) }
|
||||
/// Floored integer modulus
|
||||
#[inline] pub fn mod_floor<T: Integer>(x: T, y: T) -> T { x.mod_floor(&y) }
|
||||
/// Simultaneous floored integer division and modulus
|
||||
#[inline] pub fn div_mod_floor<T: Integer>(x: T, y: T) -> (T, T) { x.div_mod_floor(&y) }
|
||||
|
||||
/// Calculates the Greatest Common Divisor (GCD) of the number and `other`. The
|
||||
/// result is always positive.
|
||||
#[inline(always)] pub fn gcd<T: Integer>(x: T, y: T) -> T { x.gcd(&y) }
|
||||
/// Calculates the Lowest Common Multiple (LCM) of the number and `other`.
|
||||
#[inline(always)] pub fn lcm<T: Integer>(x: T, y: T) -> T { x.lcm(&y) }
|
||||
|
||||
macro_rules! impl_integer_for_int {
|
||||
($T:ty, $test_mod:ident) => (
|
||||
impl Integer for $T {
|
||||
/// Floored integer division
|
||||
#[inline]
|
||||
fn div_floor(&self, other: &$T) -> $T {
|
||||
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
|
||||
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
|
||||
match self.div_rem(other) {
|
||||
(d, r) if (r > 0 && *other < 0)
|
||||
|| (r < 0 && *other > 0) => d - 1,
|
||||
(d, _) => d,
|
||||
}
|
||||
}
|
||||
|
||||
/// Floored integer modulo
|
||||
#[inline]
|
||||
fn mod_floor(&self, other: &$T) -> $T {
|
||||
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
|
||||
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
|
||||
match *self % *other {
|
||||
r if (r > 0 && *other < 0)
|
||||
|| (r < 0 && *other > 0) => r + *other,
|
||||
r => r,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates `div_floor` and `mod_floor` simultaneously
|
||||
#[inline]
|
||||
fn div_mod_floor(&self, other: &$T) -> ($T,$T) {
|
||||
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
|
||||
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
|
||||
match self.div_rem(other) {
|
||||
(d, r) if (r > 0 && *other < 0)
|
||||
|| (r < 0 && *other > 0) => (d - 1, r + *other),
|
||||
(d, r) => (d, r),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the Greatest Common Divisor (GCD) of the number and
|
||||
/// `other`. The result is always positive.
|
||||
#[inline]
|
||||
fn gcd(&self, other: &$T) -> $T {
|
||||
// Use Euclid's algorithm
|
||||
let mut m = *self;
|
||||
let mut n = *other;
|
||||
while m != 0 {
|
||||
let temp = m;
|
||||
m = n % temp;
|
||||
n = temp;
|
||||
}
|
||||
n.abs()
|
||||
}
|
||||
|
||||
/// Calculates the Lowest Common Multiple (LCM) of the number and
|
||||
/// `other`.
|
||||
#[inline]
|
||||
fn lcm(&self, other: &$T) -> $T {
|
||||
// should not have to recalculate abs
|
||||
((*self * *other) / self.gcd(other)).abs()
|
||||
}
|
||||
|
||||
/// Deprecated, use `is_multiple_of` instead.
|
||||
#[deprecated = "function renamed to `is_multiple_of`"]
|
||||
#[inline]
|
||||
fn divides(&self, other: &$T) -> bool { return self.is_multiple_of(other); }
|
||||
|
||||
/// Returns `true` if the number is a multiple of `other`.
|
||||
#[inline]
|
||||
fn is_multiple_of(&self, other: &$T) -> bool { *self % *other == 0 }
|
||||
|
||||
/// Returns `true` if the number is divisible by `2`
|
||||
#[inline]
|
||||
fn is_even(&self) -> bool { self & 1 == 0 }
|
||||
|
||||
/// Returns `true` if the number is not divisible by `2`
|
||||
#[inline]
|
||||
fn is_odd(&self) -> bool { !self.is_even() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod $test_mod {
|
||||
use Integer;
|
||||
|
||||
/// Checks that the division rule holds for:
|
||||
///
|
||||
/// - `n`: numerator (dividend)
|
||||
/// - `d`: denominator (divisor)
|
||||
/// - `qr`: quotient and remainder
|
||||
#[cfg(test)]
|
||||
fn test_division_rule((n,d): ($T,$T), (q,r): ($T,$T)) {
|
||||
assert_eq!(d * q + r, n);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_rem() {
|
||||
fn test_nd_dr(nd: ($T,$T), qr: ($T,$T)) {
|
||||
let (n,d) = nd;
|
||||
let separate_div_rem = (n / d, n % d);
|
||||
let combined_div_rem = n.div_rem(&d);
|
||||
|
||||
assert_eq!(separate_div_rem, qr);
|
||||
assert_eq!(combined_div_rem, qr);
|
||||
|
||||
test_division_rule(nd, separate_div_rem);
|
||||
test_division_rule(nd, combined_div_rem);
|
||||
}
|
||||
|
||||
test_nd_dr(( 8, 3), ( 2, 2));
|
||||
test_nd_dr(( 8, -3), (-2, 2));
|
||||
test_nd_dr((-8, 3), (-2, -2));
|
||||
test_nd_dr((-8, -3), ( 2, -2));
|
||||
|
||||
test_nd_dr(( 1, 2), ( 0, 1));
|
||||
test_nd_dr(( 1, -2), ( 0, 1));
|
||||
test_nd_dr((-1, 2), ( 0, -1));
|
||||
test_nd_dr((-1, -2), ( 0, -1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_mod_floor() {
|
||||
fn test_nd_dm(nd: ($T,$T), dm: ($T,$T)) {
|
||||
let (n,d) = nd;
|
||||
let separate_div_mod_floor = (n.div_floor(&d), n.mod_floor(&d));
|
||||
let combined_div_mod_floor = n.div_mod_floor(&d);
|
||||
|
||||
assert_eq!(separate_div_mod_floor, dm);
|
||||
assert_eq!(combined_div_mod_floor, dm);
|
||||
|
||||
test_division_rule(nd, separate_div_mod_floor);
|
||||
test_division_rule(nd, combined_div_mod_floor);
|
||||
}
|
||||
|
||||
test_nd_dm(( 8, 3), ( 2, 2));
|
||||
test_nd_dm(( 8, -3), (-3, -1));
|
||||
test_nd_dm((-8, 3), (-3, 1));
|
||||
test_nd_dm((-8, -3), ( 2, -2));
|
||||
|
||||
test_nd_dm(( 1, 2), ( 0, 1));
|
||||
test_nd_dm(( 1, -2), (-1, -1));
|
||||
test_nd_dm((-1, 2), (-1, 1));
|
||||
test_nd_dm((-1, -2), ( 0, -1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gcd() {
|
||||
assert_eq!((10 as $T).gcd(&2), 2 as $T);
|
||||
assert_eq!((10 as $T).gcd(&3), 1 as $T);
|
||||
assert_eq!((0 as $T).gcd(&3), 3 as $T);
|
||||
assert_eq!((3 as $T).gcd(&3), 3 as $T);
|
||||
assert_eq!((56 as $T).gcd(&42), 14 as $T);
|
||||
assert_eq!((3 as $T).gcd(&-3), 3 as $T);
|
||||
assert_eq!((-6 as $T).gcd(&3), 3 as $T);
|
||||
assert_eq!((-4 as $T).gcd(&-2), 2 as $T);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lcm() {
|
||||
assert_eq!((1 as $T).lcm(&0), 0 as $T);
|
||||
assert_eq!((0 as $T).lcm(&1), 0 as $T);
|
||||
assert_eq!((1 as $T).lcm(&1), 1 as $T);
|
||||
assert_eq!((-1 as $T).lcm(&1), 1 as $T);
|
||||
assert_eq!((1 as $T).lcm(&-1), 1 as $T);
|
||||
assert_eq!((-1 as $T).lcm(&-1), 1 as $T);
|
||||
assert_eq!((8 as $T).lcm(&9), 72 as $T);
|
||||
assert_eq!((11 as $T).lcm(&5), 55 as $T);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_even() {
|
||||
assert_eq!((-4 as $T).is_even(), true);
|
||||
assert_eq!((-3 as $T).is_even(), false);
|
||||
assert_eq!((-2 as $T).is_even(), true);
|
||||
assert_eq!((-1 as $T).is_even(), false);
|
||||
assert_eq!((0 as $T).is_even(), true);
|
||||
assert_eq!((1 as $T).is_even(), false);
|
||||
assert_eq!((2 as $T).is_even(), true);
|
||||
assert_eq!((3 as $T).is_even(), false);
|
||||
assert_eq!((4 as $T).is_even(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_odd() {
|
||||
assert_eq!((-4 as $T).is_odd(), false);
|
||||
assert_eq!((-3 as $T).is_odd(), true);
|
||||
assert_eq!((-2 as $T).is_odd(), false);
|
||||
assert_eq!((-1 as $T).is_odd(), true);
|
||||
assert_eq!((0 as $T).is_odd(), false);
|
||||
assert_eq!((1 as $T).is_odd(), true);
|
||||
assert_eq!((2 as $T).is_odd(), false);
|
||||
assert_eq!((3 as $T).is_odd(), true);
|
||||
assert_eq!((4 as $T).is_odd(), false);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
impl_integer_for_int!(i8, test_integer_i8)
|
||||
impl_integer_for_int!(i16, test_integer_i16)
|
||||
impl_integer_for_int!(i32, test_integer_i32)
|
||||
impl_integer_for_int!(i64, test_integer_i64)
|
||||
impl_integer_for_int!(int, test_integer_int)
|
||||
|
||||
macro_rules! impl_integer_for_uint {
|
||||
($T:ty, $test_mod:ident) => (
|
||||
impl Integer for $T {
|
||||
/// Unsigned integer division. Returns the same result as `div` (`/`).
|
||||
#[inline]
|
||||
fn div_floor(&self, other: &$T) -> $T { *self / *other }
|
||||
|
||||
/// Unsigned integer modulo operation. Returns the same result as `rem` (`%`).
|
||||
#[inline]
|
||||
fn mod_floor(&self, other: &$T) -> $T { *self % *other }
|
||||
|
||||
/// Calculates the Greatest Common Divisor (GCD) of the number and `other`
|
||||
#[inline]
|
||||
fn gcd(&self, other: &$T) -> $T {
|
||||
// Use Euclid's algorithm
|
||||
let mut m = *self;
|
||||
let mut n = *other;
|
||||
while m != 0 {
|
||||
let temp = m;
|
||||
m = n % temp;
|
||||
n = temp;
|
||||
}
|
||||
n
|
||||
}
|
||||
|
||||
/// Calculates the Lowest Common Multiple (LCM) of the number and `other`.
|
||||
#[inline]
|
||||
fn lcm(&self, other: &$T) -> $T {
|
||||
(*self * *other) / self.gcd(other)
|
||||
}
|
||||
|
||||
/// Deprecated, use `is_multiple_of` instead.
|
||||
#[deprecated = "function renamed to `is_multiple_of`"]
|
||||
#[inline]
|
||||
fn divides(&self, other: &$T) -> bool { return self.is_multiple_of(other); }
|
||||
|
||||
/// Returns `true` if the number is a multiple of `other`.
|
||||
#[inline]
|
||||
fn is_multiple_of(&self, other: &$T) -> bool { *self % *other == 0 }
|
||||
|
||||
/// Returns `true` if the number is divisible by `2`.
|
||||
#[inline]
|
||||
fn is_even(&self) -> bool { self & 1 == 0 }
|
||||
|
||||
/// Returns `true` if the number is not divisible by `2`.
|
||||
#[inline]
|
||||
fn is_odd(&self) -> bool { !self.is_even() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod $test_mod {
|
||||
use Integer;
|
||||
|
||||
#[test]
|
||||
fn test_div_mod_floor() {
|
||||
assert_eq!((10 as $T).div_floor(&(3 as $T)), 3 as $T);
|
||||
assert_eq!((10 as $T).mod_floor(&(3 as $T)), 1 as $T);
|
||||
assert_eq!((10 as $T).div_mod_floor(&(3 as $T)), (3 as $T, 1 as $T));
|
||||
assert_eq!((5 as $T).div_floor(&(5 as $T)), 1 as $T);
|
||||
assert_eq!((5 as $T).mod_floor(&(5 as $T)), 0 as $T);
|
||||
assert_eq!((5 as $T).div_mod_floor(&(5 as $T)), (1 as $T, 0 as $T));
|
||||
assert_eq!((3 as $T).div_floor(&(7 as $T)), 0 as $T);
|
||||
assert_eq!((3 as $T).mod_floor(&(7 as $T)), 3 as $T);
|
||||
assert_eq!((3 as $T).div_mod_floor(&(7 as $T)), (0 as $T, 3 as $T));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gcd() {
|
||||
assert_eq!((10 as $T).gcd(&2), 2 as $T);
|
||||
assert_eq!((10 as $T).gcd(&3), 1 as $T);
|
||||
assert_eq!((0 as $T).gcd(&3), 3 as $T);
|
||||
assert_eq!((3 as $T).gcd(&3), 3 as $T);
|
||||
assert_eq!((56 as $T).gcd(&42), 14 as $T);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(type_overflow)]
|
||||
fn test_lcm() {
|
||||
assert_eq!((1 as $T).lcm(&0), 0 as $T);
|
||||
assert_eq!((0 as $T).lcm(&1), 0 as $T);
|
||||
assert_eq!((1 as $T).lcm(&1), 1 as $T);
|
||||
assert_eq!((8 as $T).lcm(&9), 72 as $T);
|
||||
assert_eq!((11 as $T).lcm(&5), 55 as $T);
|
||||
assert_eq!((99 as $T).lcm(&17), 1683 as $T);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_multiple_of() {
|
||||
assert!((6 as $T).is_multiple_of(&(6 as $T)));
|
||||
assert!((6 as $T).is_multiple_of(&(3 as $T)));
|
||||
assert!((6 as $T).is_multiple_of(&(1 as $T)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_even() {
|
||||
assert_eq!((0 as $T).is_even(), true);
|
||||
assert_eq!((1 as $T).is_even(), false);
|
||||
assert_eq!((2 as $T).is_even(), true);
|
||||
assert_eq!((3 as $T).is_even(), false);
|
||||
assert_eq!((4 as $T).is_even(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_odd() {
|
||||
assert_eq!((0 as $T).is_odd(), false);
|
||||
assert_eq!((1 as $T).is_odd(), true);
|
||||
assert_eq!((2 as $T).is_odd(), false);
|
||||
assert_eq!((3 as $T).is_odd(), true);
|
||||
assert_eq!((4 as $T).is_odd(), false);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
impl_integer_for_uint!(u8, test_integer_u8)
|
||||
impl_integer_for_uint!(u16, test_integer_u16)
|
||||
impl_integer_for_uint!(u32, test_integer_u32)
|
||||
impl_integer_for_uint!(u64, test_integer_u64)
|
||||
impl_integer_for_uint!(uint, test_integer_uint)
|
@ -1,73 +0,0 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Simple numerics.
|
||||
//!
|
||||
//! This crate contains arbitrary-sized integer, rational, and complex types.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! This example uses the BigRational type and [Newton's method][newt] to
|
||||
//! approximate a square root to arbitrary precision:
|
||||
//!
|
||||
//! ```
|
||||
//! # #![allow(deprecated)]
|
||||
//! extern crate num;
|
||||
//!
|
||||
//! use num::bigint::BigInt;
|
||||
//! use num::rational::{Ratio, BigRational};
|
||||
//!
|
||||
//! fn approx_sqrt(number: u64, iterations: uint) -> BigRational {
|
||||
//! let start: Ratio<BigInt> = Ratio::from_integer(FromPrimitive::from_u64(number).unwrap());
|
||||
//! let mut approx = start.clone();
|
||||
//!
|
||||
//! for _ in range(0, iterations) {
|
||||
//! approx = (approx + (start / approx)) /
|
||||
//! Ratio::from_integer(FromPrimitive::from_u64(2).unwrap());
|
||||
//! }
|
||||
//!
|
||||
//! approx
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! println!("{}", approx_sqrt(10, 4)); // prints 4057691201/1283082416
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [newt]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
|
||||
|
||||
#![allow(unknown_features)]
|
||||
#![feature(macro_rules, slicing_syntax)]
|
||||
#![feature(default_type_params)]
|
||||
|
||||
#![crate_name = "num"]
|
||||
#![deprecated = "This is now a cargo package located at: \
|
||||
https://github.com/rust-lang/num"]
|
||||
#![allow(deprecated)]
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
#![license = "MIT/ASL2"]
|
||||
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||
html_root_url = "http://doc.rust-lang.org/nightly/",
|
||||
html_playground_url = "http://play.rust-lang.org/")]
|
||||
#![allow(deprecated)] // from_str_radix
|
||||
|
||||
extern crate rand;
|
||||
|
||||
pub use bigint::{BigInt, BigUint};
|
||||
pub use rational::{Rational, BigRational};
|
||||
pub use complex::Complex;
|
||||
pub use integer::Integer;
|
||||
|
||||
pub mod bigint;
|
||||
pub mod complex;
|
||||
pub mod integer;
|
||||
pub mod rational;
|
@ -1,803 +0,0 @@
|
||||
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Rational numbers
|
||||
|
||||
use Integer;
|
||||
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::from_str::FromStr;
|
||||
use std::num;
|
||||
use std::num::{Zero, One, ToStrRadix, FromStrRadix};
|
||||
|
||||
use bigint::{BigInt, BigUint, Sign, Plus, Minus};
|
||||
|
||||
/// Represents the ratio between 2 numbers.
|
||||
#[deriving(Clone, Hash)]
|
||||
#[allow(missing_doc)]
|
||||
pub struct Ratio<T> {
|
||||
numer: T,
|
||||
denom: T
|
||||
}
|
||||
|
||||
/// Alias for a `Ratio` of machine-sized integers.
|
||||
pub type Rational = Ratio<int>;
|
||||
pub type Rational32 = Ratio<i32>;
|
||||
pub type Rational64 = Ratio<i64>;
|
||||
|
||||
/// Alias for arbitrary precision rationals.
|
||||
pub type BigRational = Ratio<BigInt>;
|
||||
|
||||
impl<T: Clone + Integer + PartialOrd>
|
||||
Ratio<T> {
|
||||
/// Creates a ratio representing the integer `t`.
|
||||
#[inline]
|
||||
pub fn from_integer(t: T) -> Ratio<T> {
|
||||
Ratio::new_raw(t, One::one())
|
||||
}
|
||||
|
||||
/// Creates a ratio without checking for `denom == 0` or reducing.
|
||||
#[inline]
|
||||
pub fn new_raw(numer: T, denom: T) -> Ratio<T> {
|
||||
Ratio { numer: numer, denom: denom }
|
||||
}
|
||||
|
||||
/// Create a new Ratio. Fails if `denom == 0`.
|
||||
#[inline]
|
||||
pub fn new(numer: T, denom: T) -> Ratio<T> {
|
||||
if denom == Zero::zero() {
|
||||
fail!("denominator == 0");
|
||||
}
|
||||
let mut ret = Ratio::new_raw(numer, denom);
|
||||
ret.reduce();
|
||||
ret
|
||||
}
|
||||
|
||||
/// Converts to an integer.
|
||||
#[inline]
|
||||
pub fn to_integer(&self) -> T {
|
||||
self.trunc().numer
|
||||
}
|
||||
|
||||
/// Gets an immutable reference to the numerator.
|
||||
#[inline]
|
||||
pub fn numer<'a>(&'a self) -> &'a T {
|
||||
&self.numer
|
||||
}
|
||||
|
||||
/// Gets an immutable reference to the denominator.
|
||||
#[inline]
|
||||
pub fn denom<'a>(&'a self) -> &'a T {
|
||||
&self.denom
|
||||
}
|
||||
|
||||
/// Returns true if the rational number is an integer (denominator is 1).
|
||||
#[inline]
|
||||
pub fn is_integer(&self) -> bool {
|
||||
self.denom == One::one()
|
||||
}
|
||||
|
||||
/// Put self into lowest terms, with denom > 0.
|
||||
fn reduce(&mut self) {
|
||||
let g : T = self.numer.gcd(&self.denom);
|
||||
|
||||
// FIXME(#5992): assignment operator overloads
|
||||
// self.numer /= g;
|
||||
self.numer = self.numer / g;
|
||||
// FIXME(#5992): assignment operator overloads
|
||||
// self.denom /= g;
|
||||
self.denom = self.denom / g;
|
||||
|
||||
// keep denom positive!
|
||||
if self.denom < Zero::zero() {
|
||||
self.numer = -self.numer;
|
||||
self.denom = -self.denom;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a `reduce`d copy of self.
|
||||
pub fn reduced(&self) -> Ratio<T> {
|
||||
let mut ret = self.clone();
|
||||
ret.reduce();
|
||||
ret
|
||||
}
|
||||
|
||||
/// Returns the reciprocal.
|
||||
#[inline]
|
||||
pub fn recip(&self) -> Ratio<T> {
|
||||
Ratio::new_raw(self.denom.clone(), self.numer.clone())
|
||||
}
|
||||
|
||||
/// Rounds towards minus infinity.
|
||||
#[inline]
|
||||
pub fn floor(&self) -> Ratio<T> {
|
||||
if *self < Zero::zero() {
|
||||
Ratio::from_integer((self.numer - self.denom + One::one()) / self.denom)
|
||||
} else {
|
||||
Ratio::from_integer(self.numer / self.denom)
|
||||
}
|
||||
}
|
||||
|
||||
/// Rounds towards plus infinity.
|
||||
#[inline]
|
||||
pub fn ceil(&self) -> Ratio<T> {
|
||||
if *self < Zero::zero() {
|
||||
Ratio::from_integer(self.numer / self.denom)
|
||||
} else {
|
||||
Ratio::from_integer((self.numer + self.denom - One::one()) / self.denom)
|
||||
}
|
||||
}
|
||||
|
||||
/// Rounds to the nearest integer. Rounds half-way cases away from zero.
|
||||
#[inline]
|
||||
pub fn round(&self) -> Ratio<T> {
|
||||
let one: T = One::one();
|
||||
let two: T = one + one;
|
||||
|
||||
// Find unsigned fractional part of rational number
|
||||
let fractional = self.fract().abs();
|
||||
|
||||
// The algorithm compares the unsigned fractional part with 1/2, that
|
||||
// is, a/b >= 1/2, or a >= b/2. For odd denominators, we use
|
||||
// a >= (b/2)+1. This avoids overflow issues.
|
||||
let half_or_larger = if fractional.denom().is_even() {
|
||||
*fractional.numer() >= *fractional.denom() / two
|
||||
} else {
|
||||
*fractional.numer() >= (*fractional.denom() / two) + one
|
||||
};
|
||||
|
||||
if half_or_larger {
|
||||
if *self >= Zero::zero() {
|
||||
self.trunc() + One::one()
|
||||
} else {
|
||||
self.trunc() - One::one()
|
||||
}
|
||||
} else {
|
||||
self.trunc()
|
||||
}
|
||||
}
|
||||
|
||||
/// Rounds towards zero.
|
||||
#[inline]
|
||||
pub fn trunc(&self) -> Ratio<T> {
|
||||
Ratio::from_integer(self.numer / self.denom)
|
||||
}
|
||||
|
||||
/// Returns the fractional part of a number.
|
||||
#[inline]
|
||||
pub fn fract(&self) -> Ratio<T> {
|
||||
Ratio::new_raw(self.numer % self.denom, self.denom.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ratio<BigInt> {
|
||||
/// Converts a float into a rational number.
|
||||
pub fn from_float<T: Float>(f: T) -> Option<BigRational> {
|
||||
if !f.is_finite() {
|
||||
return None;
|
||||
}
|
||||
let (mantissa, exponent, sign) = f.integer_decode();
|
||||
let bigint_sign: Sign = if sign == 1 { Plus } else { Minus };
|
||||
if exponent < 0 {
|
||||
let one: BigInt = One::one();
|
||||
let denom: BigInt = one << ((-exponent) as uint);
|
||||
let numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap();
|
||||
Some(Ratio::new(BigInt::from_biguint(bigint_sign, numer), denom))
|
||||
} else {
|
||||
let mut numer: BigUint = FromPrimitive::from_u64(mantissa).unwrap();
|
||||
numer = numer << (exponent as uint);
|
||||
Some(Ratio::from_integer(BigInt::from_biguint(bigint_sign, numer)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Comparisons */
|
||||
|
||||
// comparing a/b and c/d is the same as comparing a*d and b*c, so we
|
||||
// abstract that pattern. The following macro takes a trait and either
|
||||
// a comma-separated list of "method name -> return value" or just
|
||||
// "method name" (return value is bool in that case)
|
||||
macro_rules! cmp_impl {
|
||||
(impl $imp:ident, $($method:ident),+) => {
|
||||
cmp_impl!(impl $imp, $($method -> bool),+)
|
||||
};
|
||||
// return something other than a Ratio<T>
|
||||
(impl $imp:ident, $($method:ident -> $res:ty),*) => {
|
||||
impl<T: Mul<T,T> + $imp> $imp for Ratio<T> {
|
||||
$(
|
||||
#[inline]
|
||||
fn $method(&self, other: &Ratio<T>) -> $res {
|
||||
(self.numer * other.denom). $method (&(self.denom*other.numer))
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
cmp_impl!(impl PartialEq, eq, ne)
|
||||
cmp_impl!(impl PartialOrd, lt -> bool, gt -> bool, le -> bool, ge -> bool,
|
||||
partial_cmp -> Option<cmp::Ordering>)
|
||||
cmp_impl!(impl Eq, )
|
||||
cmp_impl!(impl Ord, cmp -> cmp::Ordering)
|
||||
|
||||
/* Arithmetic */
|
||||
// a/b * c/d = (a*c)/(b*d)
|
||||
impl<T: Clone + Integer + PartialOrd>
|
||||
Mul<Ratio<T>,Ratio<T>> for Ratio<T> {
|
||||
#[inline]
|
||||
fn mul(&self, rhs: &Ratio<T>) -> Ratio<T> {
|
||||
Ratio::new(self.numer * rhs.numer, self.denom * rhs.denom)
|
||||
}
|
||||
}
|
||||
|
||||
// (a/b) / (c/d) = (a*d)/(b*c)
|
||||
impl<T: Clone + Integer + PartialOrd>
|
||||
Div<Ratio<T>,Ratio<T>> for Ratio<T> {
|
||||
#[inline]
|
||||
fn div(&self, rhs: &Ratio<T>) -> Ratio<T> {
|
||||
Ratio::new(self.numer * rhs.denom, self.denom * rhs.numer)
|
||||
}
|
||||
}
|
||||
|
||||
// Abstracts the a/b `op` c/d = (a*d `op` b*d) / (b*d) pattern
|
||||
macro_rules! arith_impl {
|
||||
(impl $imp:ident, $method:ident) => {
|
||||
impl<T: Clone + Integer + PartialOrd>
|
||||
$imp<Ratio<T>,Ratio<T>> for Ratio<T> {
|
||||
#[inline]
|
||||
fn $method(&self, rhs: &Ratio<T>) -> Ratio<T> {
|
||||
Ratio::new((self.numer * rhs.denom).$method(&(self.denom * rhs.numer)),
|
||||
self.denom * rhs.denom)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a/b + c/d = (a*d + b*c)/(b*d)
|
||||
arith_impl!(impl Add, add)
|
||||
|
||||
// a/b - c/d = (a*d - b*c)/(b*d)
|
||||
arith_impl!(impl Sub, sub)
|
||||
|
||||
// a/b % c/d = (a*d % b*c)/(b*d)
|
||||
arith_impl!(impl Rem, rem)
|
||||
|
||||
impl<T: Clone + Integer + PartialOrd>
|
||||
Neg<Ratio<T>> for Ratio<T> {
|
||||
#[inline]
|
||||
fn neg(&self) -> Ratio<T> {
|
||||
Ratio::new_raw(-self.numer, self.denom.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/* Constants */
|
||||
impl<T: Clone + Integer + PartialOrd>
|
||||
Zero for Ratio<T> {
|
||||
#[inline]
|
||||
fn zero() -> Ratio<T> {
|
||||
Ratio::new_raw(Zero::zero(), One::one())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_zero(&self) -> bool {
|
||||
*self == Zero::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Integer + PartialOrd>
|
||||
One for Ratio<T> {
|
||||
#[inline]
|
||||
fn one() -> Ratio<T> {
|
||||
Ratio::new_raw(One::one(), One::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Integer + PartialOrd>
|
||||
Num for Ratio<T> {}
|
||||
|
||||
impl<T: Clone + Integer + PartialOrd>
|
||||
num::Signed for Ratio<T> {
|
||||
#[inline]
|
||||
fn abs(&self) -> Ratio<T> {
|
||||
if self.is_negative() { -self.clone() } else { self.clone() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn abs_sub(&self, other: &Ratio<T>) -> Ratio<T> {
|
||||
if *self <= *other { Zero::zero() } else { *self - *other }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn signum(&self) -> Ratio<T> {
|
||||
if *self > Zero::zero() {
|
||||
num::one()
|
||||
} else if self.is_zero() {
|
||||
num::zero()
|
||||
} else {
|
||||
- num::one::<Ratio<T>>()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_positive(&self) -> bool { *self > Zero::zero() }
|
||||
|
||||
#[inline]
|
||||
fn is_negative(&self) -> bool { *self < Zero::zero() }
|
||||
}
|
||||
|
||||
/* String conversions */
|
||||
impl<T: fmt::Show + Eq + One> fmt::Show for Ratio<T> {
|
||||
/// Renders as `numer/denom`. If denom=1, renders as numer.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.denom == One::one() {
|
||||
write!(f, "{}", self.numer)
|
||||
} else {
|
||||
write!(f, "{}/{}", self.numer, self.denom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToStrRadix> ToStrRadix for Ratio<T> {
|
||||
/// Renders as `numer/denom` where the numbers are in base `radix`.
|
||||
fn to_str_radix(&self, radix: uint) -> String {
|
||||
format!("{}/{}",
|
||||
self.numer.to_str_radix(radix),
|
||||
self.denom.to_str_radix(radix))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromStr + Clone + Integer + PartialOrd>
|
||||
FromStr for Ratio<T> {
|
||||
/// Parses `numer/denom` or just `numer`.
|
||||
fn from_str(s: &str) -> Option<Ratio<T>> {
|
||||
let mut split = s.splitn(1, '/');
|
||||
|
||||
let num = split.next().and_then(|n| FromStr::from_str(n));
|
||||
let den = split.next().or(Some("1")).and_then(|d| FromStr::from_str(d));
|
||||
|
||||
match (num, den) {
|
||||
(Some(n), Some(d)) => Some(Ratio::new(n, d)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromStrRadix + Clone + Integer + PartialOrd>
|
||||
FromStrRadix for Ratio<T> {
|
||||
/// Parses `numer/denom` where the numbers are in base `radix`.
|
||||
fn from_str_radix(s: &str, radix: uint) -> Option<Ratio<T>> {
|
||||
let split: Vec<&str> = s.splitn(1, '/').collect();
|
||||
if split.len() < 2 {
|
||||
None
|
||||
} else {
|
||||
let a_option: Option<T> = FromStrRadix::from_str_radix(
|
||||
*split.get(0),
|
||||
radix);
|
||||
a_option.and_then(|a| {
|
||||
let b_option: Option<T> =
|
||||
FromStrRadix::from_str_radix(*split.get(1), radix);
|
||||
b_option.and_then(|b| {
|
||||
Some(Ratio::new(a.clone(), b.clone()))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::{Ratio, Rational, BigRational};
|
||||
use std::num::{Zero, One, FromStrRadix, FromPrimitive, ToStrRadix};
|
||||
use std::from_str::FromStr;
|
||||
use std::hash::hash;
|
||||
use std::num;
|
||||
use std::i32;
|
||||
|
||||
pub static _0 : Rational = Ratio { numer: 0, denom: 1};
|
||||
pub static _1 : Rational = Ratio { numer: 1, denom: 1};
|
||||
pub static _2: Rational = Ratio { numer: 2, denom: 1};
|
||||
pub static _1_2: Rational = Ratio { numer: 1, denom: 2};
|
||||
pub static _3_2: Rational = Ratio { numer: 3, denom: 2};
|
||||
#[allow(non_uppercase_statics)]
|
||||
pub static _neg1_2: Rational = Ratio { numer: -1, denom: 2};
|
||||
pub static _1_3: Rational = Ratio { numer: 1, denom: 3};
|
||||
#[allow(non_uppercase_statics)]
|
||||
pub static _neg1_3: Rational = Ratio { numer: -1, denom: 3};
|
||||
pub static _2_3: Rational = Ratio { numer: 2, denom: 3};
|
||||
#[allow(non_uppercase_statics)]
|
||||
pub static _neg2_3: Rational = Ratio { numer: -2, denom: 3};
|
||||
|
||||
pub fn to_big(n: Rational) -> BigRational {
|
||||
Ratio::new(
|
||||
FromPrimitive::from_int(n.numer).unwrap(),
|
||||
FromPrimitive::from_int(n.denom).unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_test_constants() {
|
||||
// check our constants are what Ratio::new etc. would make.
|
||||
assert_eq!(_0, Zero::zero());
|
||||
assert_eq!(_1, One::one());
|
||||
assert_eq!(_2, Ratio::from_integer(2i));
|
||||
assert_eq!(_1_2, Ratio::new(1i,2i));
|
||||
assert_eq!(_3_2, Ratio::new(3i,2i));
|
||||
assert_eq!(_neg1_2, Ratio::new(-1i,2i));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_reduce() {
|
||||
let one22 = Ratio::new(2i,2);
|
||||
|
||||
assert_eq!(one22, One::one());
|
||||
}
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_new_zero() {
|
||||
let _a = Ratio::new(1i,0);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_cmp() {
|
||||
assert!(_0 == _0 && _1 == _1);
|
||||
assert!(_0 != _1 && _1 != _0);
|
||||
assert!(_0 < _1 && !(_1 < _0));
|
||||
assert!(_1 > _0 && !(_0 > _1));
|
||||
|
||||
assert!(_0 <= _0 && _1 <= _1);
|
||||
assert!(_0 <= _1 && !(_1 <= _0));
|
||||
|
||||
assert!(_0 >= _0 && _1 >= _1);
|
||||
assert!(_1 >= _0 && !(_0 >= _1));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_to_integer() {
|
||||
assert_eq!(_0.to_integer(), 0);
|
||||
assert_eq!(_1.to_integer(), 1);
|
||||
assert_eq!(_2.to_integer(), 2);
|
||||
assert_eq!(_1_2.to_integer(), 0);
|
||||
assert_eq!(_3_2.to_integer(), 1);
|
||||
assert_eq!(_neg1_2.to_integer(), 0);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_numer() {
|
||||
assert_eq!(_0.numer(), &0);
|
||||
assert_eq!(_1.numer(), &1);
|
||||
assert_eq!(_2.numer(), &2);
|
||||
assert_eq!(_1_2.numer(), &1);
|
||||
assert_eq!(_3_2.numer(), &3);
|
||||
assert_eq!(_neg1_2.numer(), &(-1));
|
||||
}
|
||||
#[test]
|
||||
fn test_denom() {
|
||||
assert_eq!(_0.denom(), &1);
|
||||
assert_eq!(_1.denom(), &1);
|
||||
assert_eq!(_2.denom(), &1);
|
||||
assert_eq!(_1_2.denom(), &2);
|
||||
assert_eq!(_3_2.denom(), &2);
|
||||
assert_eq!(_neg1_2.denom(), &2);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_is_integer() {
|
||||
assert!(_0.is_integer());
|
||||
assert!(_1.is_integer());
|
||||
assert!(_2.is_integer());
|
||||
assert!(!_1_2.is_integer());
|
||||
assert!(!_3_2.is_integer());
|
||||
assert!(!_neg1_2.is_integer());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_show() {
|
||||
assert_eq!(format!("{}", _2), "2".to_string());
|
||||
assert_eq!(format!("{}", _1_2), "1/2".to_string());
|
||||
assert_eq!(format!("{}", _0), "0".to_string());
|
||||
assert_eq!(format!("{}", Ratio::from_integer(-2i)), "-2".to_string());
|
||||
}
|
||||
|
||||
mod arith {
|
||||
use super::{_0, _1, _2, _1_2, _3_2, _neg1_2, to_big};
|
||||
use super::super::{Ratio, Rational};
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
fn test(a: Rational, b: Rational, c: Rational) {
|
||||
assert_eq!(a + b, c);
|
||||
assert_eq!(to_big(a) + to_big(b), to_big(c));
|
||||
}
|
||||
|
||||
test(_1, _1_2, _3_2);
|
||||
test(_1, _1, _2);
|
||||
test(_1_2, _3_2, _2);
|
||||
test(_1_2, _neg1_2, _0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
fn test(a: Rational, b: Rational, c: Rational) {
|
||||
assert_eq!(a - b, c);
|
||||
assert_eq!(to_big(a) - to_big(b), to_big(c))
|
||||
}
|
||||
|
||||
test(_1, _1_2, _1_2);
|
||||
test(_3_2, _1_2, _1);
|
||||
test(_1, _neg1_2, _3_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
fn test(a: Rational, b: Rational, c: Rational) {
|
||||
assert_eq!(a * b, c);
|
||||
assert_eq!(to_big(a) * to_big(b), to_big(c))
|
||||
}
|
||||
|
||||
test(_1, _1_2, _1_2);
|
||||
test(_1_2, _3_2, Ratio::new(3i,4i));
|
||||
test(_1_2, _neg1_2, Ratio::new(-1i, 4i));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div() {
|
||||
fn test(a: Rational, b: Rational, c: Rational) {
|
||||
assert_eq!(a / b, c);
|
||||
assert_eq!(to_big(a) / to_big(b), to_big(c))
|
||||
}
|
||||
|
||||
test(_1, _1_2, _2);
|
||||
test(_3_2, _1_2, _1 + _2);
|
||||
test(_1, _neg1_2, _neg1_2 + _neg1_2 + _neg1_2 + _neg1_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rem() {
|
||||
fn test(a: Rational, b: Rational, c: Rational) {
|
||||
assert_eq!(a % b, c);
|
||||
assert_eq!(to_big(a) % to_big(b), to_big(c))
|
||||
}
|
||||
|
||||
test(_3_2, _1, _1_2);
|
||||
test(_2, _neg1_2, _0);
|
||||
test(_1_2, _2, _1_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neg() {
|
||||
fn test(a: Rational, b: Rational) {
|
||||
assert_eq!(-a, b);
|
||||
assert_eq!(-to_big(a), to_big(b))
|
||||
}
|
||||
|
||||
test(_0, _0);
|
||||
test(_1_2, _neg1_2);
|
||||
test(-_1, _1);
|
||||
}
|
||||
#[test]
|
||||
fn test_zero() {
|
||||
assert_eq!(_0 + _0, _0);
|
||||
assert_eq!(_0 * _0, _0);
|
||||
assert_eq!(_0 * _1, _0);
|
||||
assert_eq!(_0 / _neg1_2, _0);
|
||||
assert_eq!(_0 - _0, _0);
|
||||
}
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_div_0() {
|
||||
let _a = _1 / _0;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round() {
|
||||
assert_eq!(_1_3.ceil(), _1);
|
||||
assert_eq!(_1_3.floor(), _0);
|
||||
assert_eq!(_1_3.round(), _0);
|
||||
assert_eq!(_1_3.trunc(), _0);
|
||||
|
||||
assert_eq!(_neg1_3.ceil(), _0);
|
||||
assert_eq!(_neg1_3.floor(), -_1);
|
||||
assert_eq!(_neg1_3.round(), _0);
|
||||
assert_eq!(_neg1_3.trunc(), _0);
|
||||
|
||||
assert_eq!(_2_3.ceil(), _1);
|
||||
assert_eq!(_2_3.floor(), _0);
|
||||
assert_eq!(_2_3.round(), _1);
|
||||
assert_eq!(_2_3.trunc(), _0);
|
||||
|
||||
assert_eq!(_neg2_3.ceil(), _0);
|
||||
assert_eq!(_neg2_3.floor(), -_1);
|
||||
assert_eq!(_neg2_3.round(), -_1);
|
||||
assert_eq!(_neg2_3.trunc(), _0);
|
||||
|
||||
assert_eq!(_1_2.ceil(), _1);
|
||||
assert_eq!(_1_2.floor(), _0);
|
||||
assert_eq!(_1_2.round(), _1);
|
||||
assert_eq!(_1_2.trunc(), _0);
|
||||
|
||||
assert_eq!(_neg1_2.ceil(), _0);
|
||||
assert_eq!(_neg1_2.floor(), -_1);
|
||||
assert_eq!(_neg1_2.round(), -_1);
|
||||
assert_eq!(_neg1_2.trunc(), _0);
|
||||
|
||||
assert_eq!(_1.ceil(), _1);
|
||||
assert_eq!(_1.floor(), _1);
|
||||
assert_eq!(_1.round(), _1);
|
||||
assert_eq!(_1.trunc(), _1);
|
||||
|
||||
// Overflow checks
|
||||
|
||||
let _neg1 = Ratio::from_integer(-1);
|
||||
let _large_rat1 = Ratio::new(i32::MAX, i32::MAX-1);
|
||||
let _large_rat2 = Ratio::new(i32::MAX-1, i32::MAX);
|
||||
let _large_rat3 = Ratio::new(i32::MIN+2, i32::MIN+1);
|
||||
let _large_rat4 = Ratio::new(i32::MIN+1, i32::MIN+2);
|
||||
let _large_rat5 = Ratio::new(i32::MIN+2, i32::MAX);
|
||||
let _large_rat6 = Ratio::new(i32::MAX, i32::MIN+2);
|
||||
let _large_rat7 = Ratio::new(1, i32::MIN+1);
|
||||
let _large_rat8 = Ratio::new(1, i32::MAX);
|
||||
|
||||
assert_eq!(_large_rat1.round(), One::one());
|
||||
assert_eq!(_large_rat2.round(), One::one());
|
||||
assert_eq!(_large_rat3.round(), One::one());
|
||||
assert_eq!(_large_rat4.round(), One::one());
|
||||
assert_eq!(_large_rat5.round(), _neg1);
|
||||
assert_eq!(_large_rat6.round(), _neg1);
|
||||
assert_eq!(_large_rat7.round(), Zero::zero());
|
||||
assert_eq!(_large_rat8.round(), Zero::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fract() {
|
||||
assert_eq!(_1.fract(), _0);
|
||||
assert_eq!(_neg1_2.fract(), _neg1_2);
|
||||
assert_eq!(_1_2.fract(), _1_2);
|
||||
assert_eq!(_3_2.fract(), _1_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recip() {
|
||||
assert_eq!(_1 * _1.recip(), _1);
|
||||
assert_eq!(_2 * _2.recip(), _1);
|
||||
assert_eq!(_1_2 * _1_2.recip(), _1);
|
||||
assert_eq!(_3_2 * _3_2.recip(), _1);
|
||||
assert_eq!(_neg1_2 * _neg1_2.recip(), _1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_from_str() {
|
||||
fn test(r: Rational, s: String) {
|
||||
assert_eq!(FromStr::from_str(s.as_slice()), Some(r));
|
||||
assert_eq!(r.to_string(), s);
|
||||
}
|
||||
test(_1, "1".to_string());
|
||||
test(_0, "0".to_string());
|
||||
test(_1_2, "1/2".to_string());
|
||||
test(_3_2, "3/2".to_string());
|
||||
test(_2, "2".to_string());
|
||||
test(_neg1_2, "-1/2".to_string());
|
||||
}
|
||||
#[test]
|
||||
fn test_from_str_fail() {
|
||||
fn test(s: &str) {
|
||||
let rational: Option<Rational> = FromStr::from_str(s);
|
||||
assert_eq!(rational, None);
|
||||
}
|
||||
|
||||
let xs = ["0 /1", "abc", "", "1/", "--1/2","3/2/1"];
|
||||
for &s in xs.iter() {
|
||||
test(s);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_from_str_radix() {
|
||||
fn test(r: Rational, s: String, n: uint) {
|
||||
assert_eq!(FromStrRadix::from_str_radix(s.as_slice(), n),
|
||||
Some(r));
|
||||
assert_eq!(r.to_str_radix(n).to_string(), s);
|
||||
}
|
||||
fn test3(r: Rational, s: String) { test(r, s, 3) }
|
||||
fn test16(r: Rational, s: String) { test(r, s, 16) }
|
||||
|
||||
test3(_1, "1/1".to_string());
|
||||
test3(_0, "0/1".to_string());
|
||||
test3(_1_2, "1/2".to_string());
|
||||
test3(_3_2, "10/2".to_string());
|
||||
test3(_2, "2/1".to_string());
|
||||
test3(_neg1_2, "-1/2".to_string());
|
||||
test3(_neg1_2 / _2, "-1/11".to_string());
|
||||
|
||||
test16(_1, "1/1".to_string());
|
||||
test16(_0, "0/1".to_string());
|
||||
test16(_1_2, "1/2".to_string());
|
||||
test16(_3_2, "3/2".to_string());
|
||||
test16(_2, "2/1".to_string());
|
||||
test16(_neg1_2, "-1/2".to_string());
|
||||
test16(_neg1_2 / _2, "-1/4".to_string());
|
||||
test16(Ratio::new(13i,15i), "d/f".to_string());
|
||||
test16(_1_2*_1_2*_1_2*_1_2, "1/10".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_radix_fail() {
|
||||
fn test(s: &str) {
|
||||
let radix: Option<Rational> = FromStrRadix::from_str_radix(s, 3);
|
||||
assert_eq!(radix, None);
|
||||
}
|
||||
|
||||
let xs = ["0 /1", "abc", "", "1/", "--1/2","3/2/1", "3/2"];
|
||||
for &s in xs.iter() {
|
||||
test(s);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_float() {
|
||||
fn test<T: Float>(given: T, (numer, denom): (&str, &str)) {
|
||||
let ratio: BigRational = Ratio::from_float(given).unwrap();
|
||||
assert_eq!(ratio, Ratio::new(
|
||||
FromStr::from_str(numer).unwrap(),
|
||||
FromStr::from_str(denom).unwrap()));
|
||||
}
|
||||
|
||||
// f32
|
||||
test(3.14159265359f32, ("13176795", "4194304"));
|
||||
test(2f32.powf(100.), ("1267650600228229401496703205376", "1"));
|
||||
test(-2f32.powf(100.), ("-1267650600228229401496703205376", "1"));
|
||||
test(1.0 / 2f32.powf(100.), ("1", "1267650600228229401496703205376"));
|
||||
test(684729.48391f32, ("1369459", "2"));
|
||||
test(-8573.5918555f32, ("-4389679", "512"));
|
||||
|
||||
// f64
|
||||
test(3.14159265359f64, ("3537118876014453", "1125899906842624"));
|
||||
test(2f64.powf(100.), ("1267650600228229401496703205376", "1"));
|
||||
test(-2f64.powf(100.), ("-1267650600228229401496703205376", "1"));
|
||||
test(684729.48391f64, ("367611342500051", "536870912"));
|
||||
test(-8573.5918555f64, ("-4713381968463931", "549755813888"));
|
||||
test(1.0 / 2f64.powf(100.), ("1", "1267650600228229401496703205376"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_float_fail() {
|
||||
use std::{f32, f64};
|
||||
|
||||
assert_eq!(Ratio::from_float(f32::NAN), None);
|
||||
assert_eq!(Ratio::from_float(f32::INFINITY), None);
|
||||
assert_eq!(Ratio::from_float(f32::NEG_INFINITY), None);
|
||||
assert_eq!(Ratio::from_float(f64::NAN), None);
|
||||
assert_eq!(Ratio::from_float(f64::INFINITY), None);
|
||||
assert_eq!(Ratio::from_float(f64::NEG_INFINITY), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signed() {
|
||||
assert_eq!(_neg1_2.abs(), _1_2);
|
||||
assert_eq!(_3_2.abs_sub(&_1_2), _1);
|
||||
assert_eq!(_1_2.abs_sub(&_3_2), Zero::zero());
|
||||
assert_eq!(_1_2.signum(), One::one());
|
||||
assert_eq!(_neg1_2.signum(), - num::one::<Ratio<int>>());
|
||||
assert!(_neg1_2.is_negative());
|
||||
assert!(! _neg1_2.is_positive());
|
||||
assert!(! _1_2.is_negative());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash() {
|
||||
assert!(hash(&_0) != hash(&_1));
|
||||
assert!(hash(&_0) != hash(&_3_2));
|
||||
}
|
||||
}
|
@ -1,462 +0,0 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Semantic version parsing and comparison.
|
||||
//!
|
||||
//! Semantic versioning (see http://semver.org/) is a set of rules for
|
||||
//! assigning version numbers intended to convey meaning about what has
|
||||
//! changed, and how much. A version number has five parts:
|
||||
//!
|
||||
//! * Major number, updated for incompatible API changes
|
||||
//! * Minor number, updated for backwards-compatible API additions
|
||||
//! * Patch number, updated for backwards-compatible bugfixes
|
||||
//! * Pre-release information (optional), preceded by a hyphen (`-`)
|
||||
//! * Build metadata (optional), preceded by a plus sign (`+`)
|
||||
//!
|
||||
//! The three mandatory components are required to be decimal numbers. The
|
||||
//! pre-release information and build metadata are required to be a
|
||||
//! period-separated list of identifiers containing only alphanumeric
|
||||
//! characters and hyphens.
|
||||
//!
|
||||
//! An example version number with all five components is
|
||||
//! `0.8.1-rc.3.0+20130922.linux`.
|
||||
|
||||
#![crate_name = "semver"]
|
||||
#![deprecated = "This is now a cargo package located at: \
|
||||
https://github.com/rust-lang/semver"]
|
||||
#![allow(deprecated)]
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
#![license = "MIT/ASL2"]
|
||||
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||
html_root_url = "http://doc.rust-lang.org/nightly/")]
|
||||
#![feature(default_type_params)]
|
||||
|
||||
use std::char;
|
||||
use std::cmp;
|
||||
use std::fmt::Show;
|
||||
use std::fmt;
|
||||
use std::hash;
|
||||
|
||||
/// An identifier in the pre-release or build metadata. If the identifier can
|
||||
/// be parsed as a decimal value, it will be represented with `Numeric`.
|
||||
#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[allow(missing_doc)]
|
||||
pub enum Identifier {
|
||||
Numeric(uint),
|
||||
AlphaNumeric(String)
|
||||
}
|
||||
|
||||
impl fmt::Show for Identifier {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Numeric(ref n) => n.fmt(f),
|
||||
AlphaNumeric(ref s) => s.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Represents a version number conforming to the semantic versioning scheme.
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct Version {
|
||||
/// The major version, to be incremented on incompatible changes.
|
||||
pub major: uint,
|
||||
/// The minor version, to be incremented when functionality is added in a
|
||||
/// backwards-compatible manner.
|
||||
pub minor: uint,
|
||||
/// The patch version, to be incremented when backwards-compatible bug
|
||||
/// fixes are made.
|
||||
pub patch: uint,
|
||||
/// The pre-release version identifier, if one exists.
|
||||
pub pre: Vec<Identifier>,
|
||||
/// The build metadata, ignored when determining version precedence.
|
||||
pub build: Vec<Identifier>,
|
||||
}
|
||||
|
||||
impl fmt::Show for Version {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(write!(f, "{}.{}.{}", self.major, self.minor, self.patch))
|
||||
if !self.pre.is_empty() {
|
||||
try!(write!(f, "-"));
|
||||
for (i, x) in self.pre.iter().enumerate() {
|
||||
if i != 0 { try!(write!(f, ".")) };
|
||||
try!(x.fmt(f));
|
||||
}
|
||||
}
|
||||
if !self.build.is_empty() {
|
||||
try!(write!(f, "+"));
|
||||
for (i, x) in self.build.iter().enumerate() {
|
||||
if i != 0 { try!(write!(f, ".")) };
|
||||
try!(x.fmt(f));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialEq for Version {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Version) -> bool {
|
||||
// We should ignore build metadata here, otherwise versions v1 and v2
|
||||
// can exist such that !(v1 < v2) && !(v1 > v2) && v1 != v2, which
|
||||
// violate strict total ordering rules.
|
||||
self.major == other.major &&
|
||||
self.minor == other.minor &&
|
||||
self.patch == other.patch &&
|
||||
self.pre == other.pre
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialOrd for Version {
|
||||
fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::Ord for Version {
|
||||
fn cmp(&self, other: &Version) -> Ordering {
|
||||
match self.major.cmp(&other.major) {
|
||||
Equal => {}
|
||||
r => return r,
|
||||
}
|
||||
|
||||
match self.minor.cmp(&other.minor) {
|
||||
Equal => {}
|
||||
r => return r,
|
||||
}
|
||||
|
||||
match self.patch.cmp(&other.patch) {
|
||||
Equal => {}
|
||||
r => return r,
|
||||
}
|
||||
|
||||
// NB: semver spec says 0.0.0-pre < 0.0.0
|
||||
// but the version of ord defined for vec
|
||||
// says that [] < [pre] so we alter it here
|
||||
match (self.pre.len(), other.pre.len()) {
|
||||
(0, 0) => Equal,
|
||||
(0, _) => Greater,
|
||||
(_, 0) => Less,
|
||||
(_, _) => self.pre.cmp(&other.pre)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: hash::Writer> hash::Hash<S> for Version {
|
||||
fn hash(&self, into: &mut S) {
|
||||
self.major.hash(into);
|
||||
self.minor.hash(into);
|
||||
self.patch.hash(into);
|
||||
self.pre.hash(into);
|
||||
}
|
||||
}
|
||||
|
||||
fn take_nonempty_prefix<T:Iterator<char>>(rdr: &mut T, pred: |char| -> bool)
|
||||
-> (String, Option<char>) {
|
||||
let mut buf = String::new();
|
||||
let mut ch = rdr.next();
|
||||
loop {
|
||||
match ch {
|
||||
None => break,
|
||||
Some(c) if !pred(c) => break,
|
||||
Some(c) => {
|
||||
buf.push_char(c);
|
||||
ch = rdr.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
(buf, ch)
|
||||
}
|
||||
|
||||
fn take_num<T: Iterator<char>>(rdr: &mut T) -> Option<(uint, Option<char>)> {
|
||||
let (s, ch) = take_nonempty_prefix(rdr, char::is_digit);
|
||||
match from_str::<uint>(s.as_slice()) {
|
||||
None => None,
|
||||
Some(i) => Some((i, ch))
|
||||
}
|
||||
}
|
||||
|
||||
fn take_ident<T: Iterator<char>>(rdr: &mut T) -> Option<(Identifier, Option<char>)> {
|
||||
let (s,ch) = take_nonempty_prefix(rdr, char::is_alphanumeric);
|
||||
if s.as_slice().chars().all(char::is_digit) {
|
||||
match from_str::<uint>(s.as_slice()) {
|
||||
None => None,
|
||||
Some(i) => Some((Numeric(i), ch))
|
||||
}
|
||||
} else {
|
||||
Some((AlphaNumeric(s), ch))
|
||||
}
|
||||
}
|
||||
|
||||
fn expect(ch: Option<char>, c: char) -> Option<()> {
|
||||
if ch != Some(c) {
|
||||
None
|
||||
} else {
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_iter<T: Iterator<char>>(rdr: &mut T) -> Option<Version> {
|
||||
let maybe_vers = take_num(rdr).and_then(|(major, ch)| {
|
||||
expect(ch, '.').and_then(|_| Some(major))
|
||||
}).and_then(|major| {
|
||||
take_num(rdr).and_then(|(minor, ch)| {
|
||||
expect(ch, '.').and_then(|_| Some((major, minor)))
|
||||
})
|
||||
}).and_then(|(major, minor)| {
|
||||
take_num(rdr).and_then(|(patch, ch)| {
|
||||
Some((major, minor, patch, ch))
|
||||
})
|
||||
});
|
||||
|
||||
let (major, minor, patch, ch) = match maybe_vers {
|
||||
Some((a, b, c, d)) => (a, b, c, d),
|
||||
None => return None
|
||||
};
|
||||
|
||||
let mut pre = vec!();
|
||||
let mut build = vec!();
|
||||
|
||||
let mut ch = ch;
|
||||
if ch == Some('-') {
|
||||
loop {
|
||||
let (id, c) = match take_ident(rdr) {
|
||||
Some((id, c)) => (id, c),
|
||||
None => return None
|
||||
};
|
||||
pre.push(id);
|
||||
ch = c;
|
||||
if ch != Some('.') { break; }
|
||||
}
|
||||
}
|
||||
|
||||
if ch == Some('+') {
|
||||
loop {
|
||||
let (id, c) = match take_ident(rdr) {
|
||||
Some((id, c)) => (id, c),
|
||||
None => return None
|
||||
};
|
||||
build.push(id);
|
||||
ch = c;
|
||||
if ch != Some('.') { break; }
|
||||
}
|
||||
}
|
||||
|
||||
Some(Version {
|
||||
major: major,
|
||||
minor: minor,
|
||||
patch: patch,
|
||||
pre: pre,
|
||||
build: build,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Parse a string into a semver object.
|
||||
pub fn parse(s: &str) -> Option<Version> {
|
||||
if !s.is_ascii() {
|
||||
return None;
|
||||
}
|
||||
let s = s.trim();
|
||||
let v = parse_iter(&mut s.chars());
|
||||
match v {
|
||||
Some(v) => {
|
||||
if v.to_string().equiv(&s) {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
assert_eq!(parse(""), None);
|
||||
assert_eq!(parse(" "), None);
|
||||
assert_eq!(parse("1"), None);
|
||||
assert_eq!(parse("1.2"), None);
|
||||
assert_eq!(parse("1.2"), None);
|
||||
assert_eq!(parse("1"), None);
|
||||
assert_eq!(parse("1.2"), None);
|
||||
assert_eq!(parse("1.2.3-"), None);
|
||||
assert_eq!(parse("a.b.c"), None);
|
||||
assert_eq!(parse("1.2.3 abc"), None);
|
||||
|
||||
assert!(parse("1.2.3") == Some(Version {
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
pre: vec!(),
|
||||
build: vec!(),
|
||||
}));
|
||||
assert!(parse(" 1.2.3 ") == Some(Version {
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
pre: vec!(),
|
||||
build: vec!(),
|
||||
}));
|
||||
assert!(parse("1.2.3-alpha1") == Some(Version {
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
pre: vec!(AlphaNumeric("alpha1".to_string())),
|
||||
build: vec!(),
|
||||
}));
|
||||
assert!(parse(" 1.2.3-alpha1 ") == Some(Version {
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
pre: vec!(AlphaNumeric("alpha1".to_string())),
|
||||
build: vec!()
|
||||
}));
|
||||
assert!(parse("1.2.3+build5") == Some(Version {
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
pre: vec!(),
|
||||
build: vec!(AlphaNumeric("build5".to_string()))
|
||||
}));
|
||||
assert!(parse(" 1.2.3+build5 ") == Some(Version {
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
pre: vec!(),
|
||||
build: vec!(AlphaNumeric("build5".to_string()))
|
||||
}));
|
||||
assert!(parse("1.2.3-alpha1+build5") == Some(Version {
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
pre: vec!(AlphaNumeric("alpha1".to_string())),
|
||||
build: vec!(AlphaNumeric("build5".to_string()))
|
||||
}));
|
||||
assert!(parse(" 1.2.3-alpha1+build5 ") == Some(Version {
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
pre: vec!(AlphaNumeric("alpha1".to_string())),
|
||||
build: vec!(AlphaNumeric("build5".to_string()))
|
||||
}));
|
||||
assert!(parse("1.2.3-1.alpha1.9+build5.7.3aedf ") == Some(Version {
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
pre: vec!(Numeric(1),AlphaNumeric("alpha1".to_string()),Numeric(9)),
|
||||
build: vec!(AlphaNumeric("build5".to_string()),
|
||||
Numeric(7),
|
||||
AlphaNumeric("3aedf".to_string()))
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
assert_eq!(parse("1.2.3"), parse("1.2.3"));
|
||||
assert_eq!(parse("1.2.3-alpha1"), parse("1.2.3-alpha1"));
|
||||
assert_eq!(parse("1.2.3+build.42"), parse("1.2.3+build.42"));
|
||||
assert_eq!(parse("1.2.3-alpha1+42"), parse("1.2.3-alpha1+42"));
|
||||
assert_eq!(parse("1.2.3+23"), parse("1.2.3+42"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ne() {
|
||||
assert!(parse("0.0.0") != parse("0.0.1"));
|
||||
assert!(parse("0.0.0") != parse("0.1.0"));
|
||||
assert!(parse("0.0.0") != parse("1.0.0"));
|
||||
assert!(parse("1.2.3-alpha") != parse("1.2.3-beta"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_show() {
|
||||
assert_eq!(format!("{}", parse("1.2.3").unwrap()),
|
||||
"1.2.3".to_string());
|
||||
assert_eq!(format!("{}", parse("1.2.3-alpha1").unwrap()),
|
||||
"1.2.3-alpha1".to_string());
|
||||
assert_eq!(format!("{}", parse("1.2.3+build.42").unwrap()),
|
||||
"1.2.3+build.42".to_string());
|
||||
assert_eq!(format!("{}", parse("1.2.3-alpha1+42").unwrap()),
|
||||
"1.2.3-alpha1+42".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_string() {
|
||||
assert_eq!(parse("1.2.3").unwrap().to_string(), "1.2.3".to_string());
|
||||
assert_eq!(parse("1.2.3-alpha1").unwrap().to_string(), "1.2.3-alpha1".to_string());
|
||||
assert_eq!(parse("1.2.3+build.42").unwrap().to_string(), "1.2.3+build.42".to_string());
|
||||
assert_eq!(parse("1.2.3-alpha1+42").unwrap().to_string(), "1.2.3-alpha1+42".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lt() {
|
||||
assert!(parse("0.0.0") < parse("1.2.3-alpha2"));
|
||||
assert!(parse("1.0.0") < parse("1.2.3-alpha2"));
|
||||
assert!(parse("1.2.0") < parse("1.2.3-alpha2"));
|
||||
assert!(parse("1.2.3-alpha1") < parse("1.2.3"));
|
||||
assert!(parse("1.2.3-alpha1") < parse("1.2.3-alpha2"));
|
||||
assert!(!(parse("1.2.3-alpha2") < parse("1.2.3-alpha2")));
|
||||
assert!(!(parse("1.2.3+23") < parse("1.2.3+42")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_le() {
|
||||
assert!(parse("0.0.0") <= parse("1.2.3-alpha2"));
|
||||
assert!(parse("1.0.0") <= parse("1.2.3-alpha2"));
|
||||
assert!(parse("1.2.0") <= parse("1.2.3-alpha2"));
|
||||
assert!(parse("1.2.3-alpha1") <= parse("1.2.3-alpha2"));
|
||||
assert!(parse("1.2.3-alpha2") <= parse("1.2.3-alpha2"));
|
||||
assert!(parse("1.2.3+23") <= parse("1.2.3+42"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gt() {
|
||||
assert!(parse("1.2.3-alpha2") > parse("0.0.0"));
|
||||
assert!(parse("1.2.3-alpha2") > parse("1.0.0"));
|
||||
assert!(parse("1.2.3-alpha2") > parse("1.2.0"));
|
||||
assert!(parse("1.2.3-alpha2") > parse("1.2.3-alpha1"));
|
||||
assert!(parse("1.2.3") > parse("1.2.3-alpha2"));
|
||||
assert!(!(parse("1.2.3-alpha2") > parse("1.2.3-alpha2")));
|
||||
assert!(!(parse("1.2.3+23") > parse("1.2.3+42")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ge() {
|
||||
assert!(parse("1.2.3-alpha2") >= parse("0.0.0"));
|
||||
assert!(parse("1.2.3-alpha2") >= parse("1.0.0"));
|
||||
assert!(parse("1.2.3-alpha2") >= parse("1.2.0"));
|
||||
assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha1"));
|
||||
assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha2"));
|
||||
assert!(parse("1.2.3+23") >= parse("1.2.3+42"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spec_order() {
|
||||
let vs = ["1.0.0-alpha",
|
||||
"1.0.0-alpha.1",
|
||||
"1.0.0-alpha.beta",
|
||||
"1.0.0-beta",
|
||||
"1.0.0-beta.2",
|
||||
"1.0.0-beta.11",
|
||||
"1.0.0-rc.1",
|
||||
"1.0.0"];
|
||||
let mut i = 1;
|
||||
while i < vs.len() {
|
||||
let a = parse(vs[i-1]).unwrap();
|
||||
let b = parse(vs[i]).unwrap();
|
||||
assert!(a < b);
|
||||
i += 1;
|
||||
}
|
||||
}
|
1243
src/liburl/lib.rs
1243
src/liburl/lib.rs
File diff suppressed because it is too large
Load Diff
@ -1,865 +0,0 @@
|
||||
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*!
|
||||
Generate and parse UUIDs
|
||||
|
||||
Provides support for Universally Unique Identifiers (UUIDs). A UUID is a
|
||||
unique 128-bit number, stored as 16 octets. UUIDs are used to assign unique
|
||||
identifiers to entities without requiring a central allocating authority.
|
||||
|
||||
They are particularly useful in distributed systems, though can be used in
|
||||
disparate areas, such as databases and network protocols. Typically a UUID is
|
||||
displayed in a readable string form as a sequence of hexadecimal digits,
|
||||
separated into groups by hyphens.
|
||||
|
||||
The uniqueness property is not strictly guaranteed, however for all practical
|
||||
purposes, it can be assumed that an unintentional collision would be extremely
|
||||
unlikely.
|
||||
|
||||
# Examples
|
||||
|
||||
To create a new random (V4) UUID and print it out in hexadecimal form:
|
||||
|
||||
```rust
|
||||
# #![allow(deprecated)]
|
||||
# extern crate uuid;
|
||||
use uuid::Uuid;
|
||||
|
||||
fn main() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
println!("{}", uuid1.to_string());
|
||||
}
|
||||
```
|
||||
|
||||
# Strings
|
||||
|
||||
Examples of string representations:
|
||||
|
||||
* simple: `936DA01F9ABD4d9d80C702AF85C822A8`
|
||||
* hyphenated: `550e8400-e29b-41d4-a716-446655440000`
|
||||
* urn: `urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4`
|
||||
|
||||
# References
|
||||
|
||||
* [Wikipedia: Universally Unique Identifier](
|
||||
http://en.wikipedia.org/wiki/Universally_unique_identifier)
|
||||
* [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace](
|
||||
http://tools.ietf.org/html/rfc4122)
|
||||
|
||||
*/
|
||||
|
||||
#![crate_name = "uuid"]
|
||||
#![deprecated = "This is now a cargo package located at: \
|
||||
https://github.com/rust-lang/uuid"]
|
||||
#![allow(deprecated)]
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
#![license = "MIT/ASL2"]
|
||||
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||
html_root_url = "http://doc.rust-lang.org/nightly/",
|
||||
html_playground_url = "http://play.rust-lang.org/")]
|
||||
|
||||
#![feature(default_type_params)]
|
||||
|
||||
// test harness access
|
||||
#[cfg(test)]
|
||||
extern crate test;
|
||||
extern crate serialize;
|
||||
|
||||
use std::char::Char;
|
||||
use std::default::Default;
|
||||
use std::fmt;
|
||||
use std::from_str::FromStr;
|
||||
use std::hash;
|
||||
use std::mem::{transmute,transmute_copy};
|
||||
use std::num::FromStrRadix;
|
||||
use std::rand;
|
||||
use std::rand::Rng;
|
||||
use std::slice;
|
||||
|
||||
use serialize::{Encoder, Encodable, Decoder, Decodable};
|
||||
|
||||
/// A 128-bit (16 byte) buffer containing the ID
|
||||
pub type UuidBytes = [u8, ..16];
|
||||
|
||||
/// The version of the UUID, denoting the generating algorithm
|
||||
#[deriving(PartialEq)]
|
||||
pub enum UuidVersion {
|
||||
/// Version 1: MAC address
|
||||
Version1Mac = 1,
|
||||
/// Version 2: DCE Security
|
||||
Version2Dce = 2,
|
||||
/// Version 3: MD5 hash
|
||||
Version3Md5 = 3,
|
||||
/// Version 4: Random
|
||||
Version4Random = 4,
|
||||
/// Version 5: SHA-1 hash
|
||||
Version5Sha1 = 5,
|
||||
}
|
||||
|
||||
/// The reserved variants of UUIDs
|
||||
#[deriving(PartialEq)]
|
||||
pub enum UuidVariant {
|
||||
/// Reserved by the NCS for backward compatibility
|
||||
VariantNCS,
|
||||
/// As described in the RFC4122 Specification (default)
|
||||
VariantRFC4122,
|
||||
/// Reserved by Microsoft for backward compatibility
|
||||
VariantMicrosoft,
|
||||
/// Reserved for future expansion
|
||||
VariantFuture,
|
||||
}
|
||||
|
||||
/// A Universally Unique Identifier (UUID)
|
||||
pub struct Uuid {
|
||||
/// The 128-bit number stored in 16 bytes
|
||||
bytes: UuidBytes
|
||||
}
|
||||
|
||||
impl<S: hash::Writer> hash::Hash<S> for Uuid {
|
||||
fn hash(&self, state: &mut S) {
|
||||
self.bytes.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
/// A UUID stored as fields (identical to UUID, used only for conversions)
|
||||
struct UuidFields {
|
||||
/// First field, 32-bit word
|
||||
data1: u32,
|
||||
/// Second field, 16-bit short
|
||||
data2: u16,
|
||||
/// Third field, 16-bit short
|
||||
data3: u16,
|
||||
/// Fourth field, 8 bytes
|
||||
data4: [u8, ..8]
|
||||
}
|
||||
|
||||
/// Error details for string parsing failures
|
||||
#[allow(missing_doc)]
|
||||
pub enum ParseError {
|
||||
ErrorInvalidLength(uint),
|
||||
ErrorInvalidCharacter(char, uint),
|
||||
ErrorInvalidGroups(uint),
|
||||
ErrorInvalidGroupLength(uint, uint, uint),
|
||||
}
|
||||
|
||||
/// Converts a ParseError to a string
|
||||
impl fmt::Show for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ErrorInvalidLength(found) =>
|
||||
write!(f, "Invalid length; expecting 32, 36 or 45 chars, \
|
||||
found {}", found),
|
||||
ErrorInvalidCharacter(found, pos) =>
|
||||
write!(f, "Invalid character; found `{}` (0x{:02x}) at \
|
||||
offset {}", found, found as uint, pos),
|
||||
ErrorInvalidGroups(found) =>
|
||||
write!(f, "Malformed; wrong number of groups: expected 1 \
|
||||
or 5, found {}", found),
|
||||
ErrorInvalidGroupLength(group, found, expecting) =>
|
||||
write!(f, "Malformed; length of group {} was {}, \
|
||||
expecting {}", group, found, expecting),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Length of each hyphenated group in hex digits
|
||||
#[allow(non_uppercase_statics)]
|
||||
static UuidGroupLens: [uint, ..5] = [8u, 4u, 4u, 4u, 12u];
|
||||
|
||||
/// UUID support
|
||||
impl Uuid {
|
||||
/// Returns a nil or empty UUID (containing all zeroes)
|
||||
pub fn nil() -> Uuid {
|
||||
let uuid = Uuid{ bytes: [0, .. 16] };
|
||||
uuid
|
||||
}
|
||||
|
||||
/// Create a new UUID of the specified version
|
||||
pub fn new(v: UuidVersion) -> Option<Uuid> {
|
||||
match v {
|
||||
Version4Random => Some(Uuid::new_v4()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new random UUID
|
||||
///
|
||||
/// Uses the `rand` module's default RNG task as the source
|
||||
/// of random numbers. Use the rand::Rand trait to supply
|
||||
/// a custom generator if required.
|
||||
pub fn new_v4() -> Uuid {
|
||||
let ub = rand::task_rng().gen_iter::<u8>().take(16).collect::<Vec<_>>();
|
||||
let mut uuid = Uuid{ bytes: [0, .. 16] };
|
||||
slice::bytes::copy_memory(uuid.bytes, ub.as_slice());
|
||||
uuid.set_variant(VariantRFC4122);
|
||||
uuid.set_version(Version4Random);
|
||||
uuid
|
||||
}
|
||||
|
||||
/// Creates a UUID using the supplied field values
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `d1` A 32-bit word
|
||||
/// * `d2` A 16-bit word
|
||||
/// * `d3` A 16-bit word
|
||||
/// * `d4` Array of 8 octets
|
||||
pub fn from_fields(d1: u32, d2: u16, d3: u16, d4: &[u8]) -> Uuid {
|
||||
// First construct a temporary field-based struct
|
||||
let mut fields = UuidFields {
|
||||
data1: 0,
|
||||
data2: 0,
|
||||
data3: 0,
|
||||
data4: [0, ..8]
|
||||
};
|
||||
|
||||
fields.data1 = d1.to_be();
|
||||
fields.data2 = d2.to_be();
|
||||
fields.data3 = d3.to_be();
|
||||
slice::bytes::copy_memory(fields.data4, d4);
|
||||
|
||||
unsafe {
|
||||
transmute(fields)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a UUID using the supplied bytes
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `b` An array or slice of 16 bytes
|
||||
pub fn from_bytes(b: &[u8]) -> Option<Uuid> {
|
||||
if b.len() != 16 {
|
||||
return None
|
||||
}
|
||||
|
||||
let mut uuid = Uuid{ bytes: [0, .. 16] };
|
||||
slice::bytes::copy_memory(uuid.bytes, b);
|
||||
Some(uuid)
|
||||
}
|
||||
|
||||
/// Specifies the variant of the UUID structure
|
||||
fn set_variant(&mut self, v: UuidVariant) {
|
||||
// Octet 8 contains the variant in the most significant 3 bits
|
||||
match v {
|
||||
VariantNCS => // b0xx...
|
||||
self.bytes[8] = self.bytes[8] & 0x7f,
|
||||
VariantRFC4122 => // b10x...
|
||||
self.bytes[8] = (self.bytes[8] & 0x3f) | 0x80,
|
||||
VariantMicrosoft => // b110...
|
||||
self.bytes[8] = (self.bytes[8] & 0x1f) | 0xc0,
|
||||
VariantFuture => // b111...
|
||||
self.bytes[8] = (self.bytes[8] & 0x1f) | 0xe0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the variant of the UUID structure
|
||||
///
|
||||
/// This determines the interpretation of the structure of the UUID.
|
||||
/// Currently only the RFC4122 variant is generated by this module.
|
||||
///
|
||||
/// * [Variant Reference](http://tools.ietf.org/html/rfc4122#section-4.1.1)
|
||||
pub fn get_variant(&self) -> Option<UuidVariant> {
|
||||
if self.bytes[8] & 0x80 == 0x00 {
|
||||
Some(VariantNCS)
|
||||
} else if self.bytes[8] & 0xc0 == 0x80 {
|
||||
Some(VariantRFC4122)
|
||||
} else if self.bytes[8] & 0xe0 == 0xc0 {
|
||||
Some(VariantMicrosoft)
|
||||
} else if self.bytes[8] & 0xe0 == 0xe0 {
|
||||
Some(VariantFuture)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies the version number of the UUID
|
||||
fn set_version(&mut self, v: UuidVersion) {
|
||||
self.bytes[6] = (self.bytes[6] & 0xF) | ((v as u8) << 4);
|
||||
}
|
||||
|
||||
/// Returns the version number of the UUID
|
||||
///
|
||||
/// This represents the algorithm used to generate the contents.
|
||||
///
|
||||
/// Currently only the Random (V4) algorithm is supported by this
|
||||
/// module. There are security and privacy implications for using
|
||||
/// older versions - see [Wikipedia: Universally Unique Identifier](
|
||||
/// http://en.wikipedia.org/wiki/Universally_unique_identifier) for
|
||||
/// details.
|
||||
///
|
||||
/// * [Version Reference](http://tools.ietf.org/html/rfc4122#section-4.1.3)
|
||||
pub fn get_version_num(&self) -> uint {
|
||||
(self.bytes[6] >> 4) as uint
|
||||
}
|
||||
|
||||
/// Returns the version of the UUID
|
||||
///
|
||||
/// This represents the algorithm used to generate the contents
|
||||
pub fn get_version(&self) -> Option<UuidVersion> {
|
||||
let v = self.bytes[6] >> 4;
|
||||
match v {
|
||||
1 => Some(Version1Mac),
|
||||
2 => Some(Version2Dce),
|
||||
3 => Some(Version3Md5),
|
||||
4 => Some(Version4Random),
|
||||
5 => Some(Version5Sha1),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an array of 16 octets containing the UUID data
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.bytes.as_slice()
|
||||
}
|
||||
|
||||
/// Returns the UUID as a string of 16 hexadecimal digits
|
||||
///
|
||||
/// Example: `936DA01F9ABD4d9d80C702AF85C822A8`
|
||||
pub fn to_simple_str(&self) -> String {
|
||||
let mut s: Vec<u8> = Vec::from_elem(32, 0u8);
|
||||
for i in range(0u, 16u) {
|
||||
let digit = format!("{:02x}", self.bytes[i] as uint);
|
||||
*s.get_mut(i*2+0) = digit.as_bytes()[0];
|
||||
*s.get_mut(i*2+1) = digit.as_bytes()[1];
|
||||
}
|
||||
String::from_utf8(s).unwrap()
|
||||
}
|
||||
|
||||
/// Returns a string of hexadecimal digits, separated into groups with a hyphen.
|
||||
///
|
||||
/// Example: `550e8400-e29b-41d4-a716-446655440000`
|
||||
pub fn to_hyphenated_str(&self) -> String {
|
||||
// Convert to field-based struct as it matches groups in output.
|
||||
// Ensure fields are in network byte order, as per RFC.
|
||||
let mut uf: UuidFields;
|
||||
unsafe {
|
||||
uf = transmute_copy(&self.bytes);
|
||||
}
|
||||
uf.data1 = uf.data1.to_be();
|
||||
uf.data2 = uf.data2.to_be();
|
||||
uf.data3 = uf.data3.to_be();
|
||||
let s = format!("{:08x}-{:04x}-{:04x}-{:02x}{:02x}-\
|
||||
{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
uf.data1,
|
||||
uf.data2, uf.data3,
|
||||
uf.data4[0], uf.data4[1],
|
||||
uf.data4[2], uf.data4[3], uf.data4[4],
|
||||
uf.data4[5], uf.data4[6], uf.data4[7]);
|
||||
s
|
||||
}
|
||||
|
||||
/// Returns the UUID formatted as a full URN string
|
||||
///
|
||||
/// This is the same as the hyphenated format, but with the "urn:uuid:" prefix.
|
||||
///
|
||||
/// Example: `urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4`
|
||||
pub fn to_urn_str(&self) -> String {
|
||||
format!("urn:uuid:{}", self.to_hyphenated_str())
|
||||
}
|
||||
|
||||
/// Parses a UUID from a string of hexadecimal digits with optional hyphens
|
||||
///
|
||||
/// Any of the formats generated by this module (simple, hyphenated, urn) are
|
||||
/// supported by this parsing function.
|
||||
pub fn parse_string(us: &str) -> Result<Uuid, ParseError> {
|
||||
|
||||
let mut us = us.clone();
|
||||
let orig_len = us.len();
|
||||
|
||||
// Ensure length is valid for any of the supported formats
|
||||
if orig_len != 32 && orig_len != 36 && orig_len != 45 {
|
||||
return Err(ErrorInvalidLength(orig_len));
|
||||
}
|
||||
|
||||
// Strip off URN prefix if present
|
||||
if us.starts_with("urn:uuid:") {
|
||||
us = us.slice(9, orig_len);
|
||||
}
|
||||
|
||||
// Make sure all chars are either hex digits or hyphen
|
||||
for (i, c) in us.chars().enumerate() {
|
||||
match c {
|
||||
'0'...'9' | 'A'...'F' | 'a'...'f' | '-' => {},
|
||||
_ => return Err(ErrorInvalidCharacter(c, i)),
|
||||
}
|
||||
}
|
||||
|
||||
// Split string up by hyphens into groups
|
||||
let hex_groups: Vec<&str> = us.split_str("-").collect();
|
||||
|
||||
// Get the length of each group
|
||||
let group_lens: Vec<uint> = hex_groups.iter().map(|&v| v.len()).collect();
|
||||
|
||||
// Ensure the group lengths are valid
|
||||
match group_lens.len() {
|
||||
// Single group, no hyphens
|
||||
1 => {
|
||||
if group_lens[0] != 32 {
|
||||
return Err(ErrorInvalidLength(group_lens[0]));
|
||||
}
|
||||
},
|
||||
// Five groups, hyphens in between each
|
||||
5 => {
|
||||
// Ensure each group length matches the expected
|
||||
for (i, (&gl, &expected)) in
|
||||
group_lens.iter().zip(UuidGroupLens.iter()).enumerate() {
|
||||
if gl != expected {
|
||||
return Err(ErrorInvalidGroupLength(i, gl, expected))
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(ErrorInvalidGroups(group_lens.len()));
|
||||
}
|
||||
}
|
||||
|
||||
// Normalise into one long hex string
|
||||
let vs = hex_groups.concat();
|
||||
|
||||
// At this point, we know we have a valid hex string, without hyphens
|
||||
assert!(vs.len() == 32);
|
||||
assert!(vs.as_slice().chars().all(|c| c.is_digit_radix(16)));
|
||||
|
||||
// Allocate output UUID buffer
|
||||
let mut ub = [0u8, ..16];
|
||||
|
||||
// Extract each hex digit from the string
|
||||
for i in range(0u, 16u) {
|
||||
ub[i] = FromStrRadix::from_str_radix(vs.as_slice()
|
||||
.slice(i*2, (i+1)*2),
|
||||
16).unwrap();
|
||||
}
|
||||
|
||||
Ok(Uuid::from_bytes(ub).unwrap())
|
||||
}
|
||||
|
||||
/// Tests if the UUID is nil
|
||||
pub fn is_nil(&self) -> bool {
|
||||
return self.bytes.iter().all(|&b| b == 0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Uuid {
|
||||
/// Returns the nil UUID, which is all zeroes
|
||||
fn default() -> Uuid {
|
||||
Uuid::nil()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Uuid {
|
||||
/// Returns a copy of the UUID
|
||||
fn clone(&self) -> Uuid { *self }
|
||||
}
|
||||
|
||||
impl FromStr for Uuid {
|
||||
/// Parse a hex string and interpret as a UUID
|
||||
///
|
||||
/// Accepted formats are a sequence of 32 hexadecimal characters,
|
||||
/// with or without hyphens (grouped as 8, 4, 4, 4, 12).
|
||||
fn from_str(us: &str) -> Option<Uuid> {
|
||||
let result = Uuid::parse_string(us);
|
||||
match result {
|
||||
Ok(u) => Some(u),
|
||||
Err(_) => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the UUID to a hexadecimal-based string representation
|
||||
impl fmt::Show for Uuid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.to_simple_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Test two UUIDs for equality
|
||||
///
|
||||
/// UUIDs are equal only when they are byte-for-byte identical
|
||||
impl PartialEq for Uuid {
|
||||
fn eq(&self, other: &Uuid) -> bool {
|
||||
self.bytes == other.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Uuid {}
|
||||
|
||||
// FIXME #9845: Test these more thoroughly
|
||||
impl<T: Encoder<E>, E> Encodable<T, E> for Uuid {
|
||||
/// Encode a UUID as a hyphenated string
|
||||
fn encode(&self, e: &mut T) -> Result<(), E> {
|
||||
e.emit_str(self.to_hyphenated_str().as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decoder<E>, E> Decodable<T, E> for Uuid {
|
||||
/// Decode a UUID from a string
|
||||
fn decode(d: &mut T) -> Result<Uuid, E> {
|
||||
match from_str(try!(d.read_str()).as_slice()) {
|
||||
Some(decode) => Ok(decode),
|
||||
None => Err(d.error("Unable to decode UUID"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a random instance of UUID (V4 conformant)
|
||||
impl rand::Rand for Uuid {
|
||||
#[inline]
|
||||
fn rand<R: rand::Rng>(rng: &mut R) -> Uuid {
|
||||
let ub = rng.gen_iter::<u8>().take(16).collect::<Vec<_>>();
|
||||
let mut uuid = Uuid{ bytes: [0, .. 16] };
|
||||
slice::bytes::copy_memory(uuid.bytes, ub.as_slice());
|
||||
uuid.set_variant(VariantRFC4122);
|
||||
uuid.set_version(Version4Random);
|
||||
uuid
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod uuidtest {
|
||||
use super::{Uuid, VariantMicrosoft, VariantNCS, VariantRFC4122,
|
||||
Version1Mac, Version2Dce, Version3Md5, Version4Random,
|
||||
Version5Sha1};
|
||||
use std::rand;
|
||||
|
||||
#[test]
|
||||
fn test_nil() {
|
||||
let nil = Uuid::nil();
|
||||
let not_nil = Uuid::new_v4();
|
||||
|
||||
assert!(nil.is_nil());
|
||||
assert!(!not_nil.is_nil());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
// Supported
|
||||
let uuid1 = Uuid::new(Version4Random).unwrap();
|
||||
let s = uuid1.to_simple_str();
|
||||
|
||||
assert!(s.len() == 32);
|
||||
assert!(uuid1.get_version().unwrap() == Version4Random);
|
||||
|
||||
// Test unsupported versions
|
||||
assert!(Uuid::new(Version1Mac) == None);
|
||||
assert!(Uuid::new(Version2Dce) == None);
|
||||
assert!(Uuid::new(Version3Md5) == None);
|
||||
assert!(Uuid::new(Version5Sha1) == None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_v4() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
|
||||
assert!(uuid1.get_version().unwrap() == Version4Random);
|
||||
assert!(uuid1.get_variant().unwrap() == VariantRFC4122);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_version() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
|
||||
assert!(uuid1.get_version().unwrap() == Version4Random);
|
||||
assert!(uuid1.get_version_num() == 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_variant() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
let uuid2 = Uuid::parse_string("550e8400-e29b-41d4-a716-446655440000").unwrap();
|
||||
let uuid3 = Uuid::parse_string("67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap();
|
||||
let uuid4 = Uuid::parse_string("936DA01F9ABD4d9dC0C702AF85C822A8").unwrap();
|
||||
let uuid5 = Uuid::parse_string("F9168C5E-CEB2-4faa-D6BF-329BF39FA1E4").unwrap();
|
||||
let uuid6 = Uuid::parse_string("f81d4fae-7dec-11d0-7765-00a0c91e6bf6").unwrap();
|
||||
|
||||
assert!(uuid1.get_variant().unwrap() == VariantRFC4122);
|
||||
assert!(uuid2.get_variant().unwrap() == VariantRFC4122);
|
||||
assert!(uuid3.get_variant().unwrap() == VariantRFC4122);
|
||||
assert!(uuid4.get_variant().unwrap() == VariantMicrosoft);
|
||||
assert!(uuid5.get_variant().unwrap() == VariantMicrosoft);
|
||||
assert!(uuid6.get_variant().unwrap() == VariantNCS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_uuid_v4() {
|
||||
use super::{ErrorInvalidCharacter, ErrorInvalidGroups,
|
||||
ErrorInvalidGroupLength, ErrorInvalidLength};
|
||||
|
||||
// Invalid
|
||||
assert!(Uuid::parse_string("").is_err());
|
||||
assert!(Uuid::parse_string("!").is_err());
|
||||
assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45").is_err());
|
||||
assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4").is_err());
|
||||
assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4").is_err());
|
||||
assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4").is_err());
|
||||
assert!(Uuid::parse_string("F9168C5E-CEB2-4faa").is_err());
|
||||
assert!(Uuid::parse_string("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4").is_err());
|
||||
assert!(Uuid::parse_string("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4").is_err());
|
||||
assert!(Uuid::parse_string("01020304-1112-2122-3132-41424344").is_err());
|
||||
assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c").is_err());
|
||||
assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c88").is_err());
|
||||
assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0cg8").is_err());
|
||||
assert!(Uuid::parse_string("67e5504410b1426%9247bb680e5fe0c8").is_err());
|
||||
|
||||
// Valid
|
||||
assert!(Uuid::parse_string("00000000000000000000000000000000").is_ok());
|
||||
assert!(Uuid::parse_string("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
|
||||
assert!(Uuid::parse_string("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
|
||||
assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").is_ok());
|
||||
assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c8").is_ok());
|
||||
assert!(Uuid::parse_string("01020304-1112-2122-3132-414243444546").is_ok());
|
||||
assert!(Uuid::parse_string("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
|
||||
|
||||
// Nil
|
||||
let nil = Uuid::nil();
|
||||
assert!(Uuid::parse_string("00000000000000000000000000000000").unwrap() == nil);
|
||||
assert!(Uuid::parse_string("00000000-0000-0000-0000-000000000000").unwrap() == nil);
|
||||
|
||||
// Round-trip
|
||||
let uuid_orig = Uuid::new_v4();
|
||||
let orig_str = uuid_orig.to_string();
|
||||
let uuid_out = Uuid::parse_string(orig_str.as_slice()).unwrap();
|
||||
assert!(uuid_orig == uuid_out);
|
||||
|
||||
// Test error reporting
|
||||
let e = Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c").unwrap_err();
|
||||
assert!(match e { ErrorInvalidLength(n) => n==31, _ => false });
|
||||
|
||||
let e = Uuid::parse_string("67e550X410b1426f9247bb680e5fe0cd").unwrap_err();
|
||||
assert!(match e { ErrorInvalidCharacter(c, n) => c=='X' && n==6, _ => false });
|
||||
|
||||
let e = Uuid::parse_string("67e550-4105b1426f9247bb680e5fe0c").unwrap_err();
|
||||
assert!(match e { ErrorInvalidGroups(n) => n==2, _ => false });
|
||||
|
||||
let e = Uuid::parse_string("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4").unwrap_err();
|
||||
assert!(match e { ErrorInvalidGroupLength(g, n, e) => g==3 && n==5 && e==4, _ => false });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_simple_str() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
let s = uuid1.to_simple_str();
|
||||
|
||||
assert!(s.len() == 32);
|
||||
assert!(s.as_slice().chars().all(|c| c.is_digit_radix(16)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_string() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
let s = uuid1.to_string();
|
||||
|
||||
assert!(s.len() == 32);
|
||||
assert!(s.as_slice().chars().all(|c| c.is_digit_radix(16)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_hyphenated_str() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
let s = uuid1.to_hyphenated_str();
|
||||
|
||||
assert!(s.len() == 36);
|
||||
assert!(s.as_slice().chars().all(|c| c.is_digit_radix(16) || c == '-'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_urn_str() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
let ss = uuid1.to_urn_str();
|
||||
let s = ss.as_slice().slice(9, ss.len());
|
||||
|
||||
assert!(ss.as_slice().starts_with("urn:uuid:"));
|
||||
assert!(s.len() == 36);
|
||||
assert!(s.as_slice()
|
||||
.chars()
|
||||
.all(|c| c.is_digit_radix(16) || c == '-'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_str_matching() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
|
||||
let hs = uuid1.to_hyphenated_str();
|
||||
let ss = uuid1.to_string();
|
||||
|
||||
let hsn = String::from_chars(hs.as_slice()
|
||||
.chars()
|
||||
.filter(|&c| c != '-')
|
||||
.collect::<Vec<char>>()
|
||||
.as_slice());
|
||||
|
||||
assert!(hsn == ss);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_roundtrip() {
|
||||
let uuid = Uuid::new_v4();
|
||||
|
||||
let hs = uuid.to_hyphenated_str();
|
||||
let uuid_hs = Uuid::parse_string(hs.as_slice()).unwrap();
|
||||
assert!(uuid_hs == uuid);
|
||||
|
||||
let ss = uuid.to_string();
|
||||
let uuid_ss = Uuid::parse_string(ss.as_slice()).unwrap();
|
||||
assert!(uuid_ss == uuid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compare() {
|
||||
let uuid1 = Uuid::new_v4();
|
||||
let uuid2 = Uuid::new_v4();
|
||||
|
||||
assert!(uuid1 == uuid1);
|
||||
assert!(uuid2 == uuid2);
|
||||
assert!(uuid1 != uuid2);
|
||||
assert!(uuid2 != uuid1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_fields() {
|
||||
let d1: u32 = 0xa1a2a3a4;
|
||||
let d2: u16 = 0xb1b2;
|
||||
let d3: u16 = 0xc1c2;
|
||||
let d4: Vec<u8> = vec!(0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8);
|
||||
|
||||
let u = Uuid::from_fields(d1, d2, d3, d4.as_slice());
|
||||
|
||||
let expected = "a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8".to_string();
|
||||
let result = u.to_simple_str();
|
||||
assert!(result == expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_bytes() {
|
||||
let b = vec!( 0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2,
|
||||
0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8 );
|
||||
|
||||
let u = Uuid::from_bytes(b.as_slice()).unwrap();
|
||||
let expected = "a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8".to_string();
|
||||
|
||||
assert!(u.to_simple_str() == expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_bytes() {
|
||||
let u = Uuid::new_v4();
|
||||
let ub = u.as_bytes();
|
||||
|
||||
assert!(ub.len() == 16);
|
||||
assert!(! ub.iter().all(|&b| b == 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bytes_roundtrip() {
|
||||
let b_in: [u8, ..16] = [ 0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2,
|
||||
0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8 ];
|
||||
|
||||
let u = Uuid::from_bytes(b_in.clone()).unwrap();
|
||||
|
||||
let b_out = u.as_bytes();
|
||||
|
||||
assert!(b_in == b_out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_operator_eq() {
|
||||
let u1 = Uuid::new_v4();
|
||||
let u2 = u1.clone();
|
||||
let u3 = Uuid::new_v4();
|
||||
|
||||
assert!(u1 == u1);
|
||||
assert!(u1 == u2);
|
||||
assert!(u2 == u1);
|
||||
|
||||
assert!(u1 != u3);
|
||||
assert!(u3 != u1);
|
||||
assert!(u2 != u3);
|
||||
assert!(u3 != u2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rand_rand() {
|
||||
let mut rng = rand::task_rng();
|
||||
let u: Uuid = rand::Rand::rand(&mut rng);
|
||||
let ub = u.as_bytes();
|
||||
|
||||
assert!(ub.len() == 16);
|
||||
assert!(! ub.iter().all(|&b| b == 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_round_trip() {
|
||||
use serialize::json;
|
||||
|
||||
let u = Uuid::new_v4();
|
||||
let s = json::encode(&u);
|
||||
let u2 = json::decode(s.as_slice()).unwrap();
|
||||
assert_eq!(u, u2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_decode() {
|
||||
use serialize::json;
|
||||
use serialize::{Decodable};
|
||||
|
||||
let js_good = json::String("a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7a8".to_string());
|
||||
let js_bad1 = json::String("a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7ah".to_string());
|
||||
let js_bad2 = json::String("a1a2a3a4a5a6a7a8a1a2a3a4a5a6a7a".to_string());
|
||||
|
||||
let u_good: Result<Uuid, _> = Decodable::decode(&mut json::Decoder::new(js_good));
|
||||
let u_bad1: Result<Uuid, _> = Decodable::decode(&mut json::Decoder::new(js_bad1));
|
||||
let u_bad2: Result<Uuid, _> = Decodable::decode(&mut json::Decoder::new(js_bad2));
|
||||
assert!(u_good.is_ok());
|
||||
assert!(u_bad1.is_err());
|
||||
assert!(u_bad2.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterbytes_impl_for_uuid() {
|
||||
use std::collections::HashSet;
|
||||
let mut set = HashSet::new();
|
||||
let id1 = Uuid::new_v4();
|
||||
let id2 = Uuid::new_v4();
|
||||
set.insert(id1);
|
||||
assert!(set.contains(&id1));
|
||||
assert!(!set.contains(&id2));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod bench {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
use super::Uuid;
|
||||
|
||||
#[bench]
|
||||
pub fn create_uuids(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
Uuid::new_v4();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
pub fn uuid_to_string(b: &mut Bencher) {
|
||||
let u = Uuid::new_v4();
|
||||
b.iter(|| {
|
||||
u.to_string();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
pub fn parse_str(b: &mut Bencher) {
|
||||
let s = "urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4";
|
||||
b.iter(|| {
|
||||
Uuid::parse_string(s).unwrap();
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user