diff --git a/Cargo.toml b/Cargo.toml index dc89349..5ef3ccd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,12 +23,16 @@ optional = true path = "bigint" [dependencies.num-complex] -optional = false +optional = true path = "complex" [dependencies.num-integer] path = "./integer" +[dependencies.num-iter] +optional = false +path = "iter" + [dependencies.num-rational] optional = true path = "rational" @@ -55,6 +59,6 @@ version = "0.3.8" [features] bigint = ["num-bigint"] -complex = [] -default = ["bigint", "complex", "rand", "rational", "rustc-serialize"] +complex = ["num-complex"] rational = ["num-rational"] +default = ["bigint", "complex", "rand", "rational", "rustc-serialize"] diff --git a/iter/Cargo.toml b/iter/Cargo.toml new file mode 100644 index 0000000..0c69411 --- /dev/null +++ b/iter/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["Ɓukasz Jan Niemier "] +name = "num-iter" +version = "0.1.0" + +[dependencies] + +[dependencies.num-integer] +optional = false +path = "../integer" + +[dependencies.num-traits] +optional = false +path = "../traits" diff --git a/iter/src/lib.rs b/iter/src/lib.rs new file mode 100644 index 0000000..8f567d7 --- /dev/null +++ b/iter/src/lib.rs @@ -0,0 +1,376 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! External iterators for generic mathematics + +extern crate num_traits as traits; +extern crate num_integer as integer; + +use integer::Integer; +use traits::{Zero, One, CheckedAdd, ToPrimitive}; +use std::ops::{Add, Sub}; + +/// An iterator over the range [start, stop) +#[derive(Clone)] +pub struct Range { + state: A, + stop: A, + one: A +} + +/// Returns an iterator over the given range [start, stop) (that is, starting +/// at start (inclusive), and ending at stop (exclusive)). +/// +/// # Example +/// +/// ```rust +/// use num::iter; +/// +/// let array = [0, 1, 2, 3, 4]; +/// +/// for i in iter::range(0, 5) { +/// println!("{}", i); +/// assert_eq!(i, array[i]); +/// } +/// ``` +#[inline] +pub fn range(start: A, stop: A) -> Range + where A: Add + PartialOrd + Clone + One +{ + Range{state: start, stop: stop, one: One::one()} +} + +// FIXME: rust-lang/rust#10414: Unfortunate type bound +impl Iterator for Range + where A: Add + PartialOrd + Clone + ToPrimitive +{ + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + if self.state < self.stop { + let result = self.state.clone(); + self.state = self.state.clone() + self.one.clone(); + Some(result) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + // This first checks if the elements are representable as i64. If they aren't, try u64 (to + // handle cases like range(huge, huger)). We don't use usize/int because the difference of + // the i64/u64 might lie within their range. + let bound = match self.state.to_i64() { + Some(a) => { + let sz = self.stop.to_i64().map(|b| b.checked_sub(a)); + match sz { + Some(Some(bound)) => bound.to_usize(), + _ => None, + } + }, + None => match self.state.to_u64() { + Some(a) => { + let sz = self.stop.to_u64().map(|b| b.checked_sub(a)); + match sz { + Some(Some(bound)) => bound.to_usize(), + _ => None + } + }, + None => None + } + }; + + match bound { + Some(b) => (b, Some(b)), + // Standard fallback for unbounded/unrepresentable bounds + None => (0, None) + } + } +} + +/// `Integer` is required to ensure the range will be the same regardless of +/// the direction it is consumed. +impl DoubleEndedIterator for Range + where A: Integer + Clone + ToPrimitive +{ + #[inline] + fn next_back(&mut self) -> Option { + if self.stop > self.state { + self.stop = self.stop.clone() - self.one.clone(); + Some(self.stop.clone()) + } else { + None + } + } +} + +/// An iterator over the range [start, stop] +#[derive(Clone)] +pub struct RangeInclusive { + range: Range, + done: bool, +} + +/// Return an iterator over the range [start, stop] +#[inline] +pub fn range_inclusive(start: A, stop: A) -> RangeInclusive + where A: Add + PartialOrd + Clone + One +{ + RangeInclusive{range: range(start, stop), done: false} +} + +impl Iterator for RangeInclusive + where A: Add + PartialOrd + Clone + ToPrimitive +{ + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + match self.range.next() { + Some(x) => Some(x), + None => { + if !self.done && self.range.state == self.range.stop { + self.done = true; + Some(self.range.stop.clone()) + } else { + None + } + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (lo, hi) = self.range.size_hint(); + if self.done { + (lo, hi) + } else { + let lo = lo.saturating_add(1); + let hi = match hi { + Some(x) => x.checked_add(1), + None => None + }; + (lo, hi) + } + } +} + +impl DoubleEndedIterator for RangeInclusive + where A: Sub + Integer + Clone + ToPrimitive +{ + #[inline] + fn next_back(&mut self) -> Option { + if self.range.stop > self.range.state { + let result = self.range.stop.clone(); + self.range.stop = self.range.stop.clone() - self.range.one.clone(); + Some(result) + } else if !self.done && self.range.state == self.range.stop { + self.done = true; + Some(self.range.stop.clone()) + } else { + None + } + } +} + +/// An iterator over the range [start, stop) by `step`. It handles overflow by stopping. +#[derive(Clone)] +pub struct RangeStep { + state: A, + stop: A, + step: A, + rev: bool, +} + +/// Return an iterator over the range [start, stop) by `step`. It handles overflow by stopping. +#[inline] +pub fn range_step(start: A, stop: A, step: A) -> RangeStep + where A: CheckedAdd + PartialOrd + Clone + Zero +{ + let rev = step < Zero::zero(); + RangeStep{state: start, stop: stop, step: step, rev: rev} +} + +impl Iterator for RangeStep + where A: CheckedAdd + PartialOrd + Clone +{ + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + if (self.rev && self.state > self.stop) || (!self.rev && self.state < self.stop) { + let result = self.state.clone(); + match self.state.checked_add(&self.step) { + Some(x) => self.state = x, + None => self.state = self.stop.clone() + } + Some(result) + } else { + None + } + } +} + +/// An iterator over the range [start, stop] by `step`. It handles overflow by stopping. +#[derive(Clone)] +pub struct RangeStepInclusive { + state: A, + stop: A, + step: A, + rev: bool, + done: bool, +} + +/// Return an iterator over the range [start, stop] by `step`. It handles overflow by stopping. +#[inline] +pub fn range_step_inclusive(start: A, stop: A, step: A) -> RangeStepInclusive + where A: CheckedAdd + PartialOrd + Clone + Zero +{ + let rev = step < Zero::zero(); + RangeStepInclusive{state: start, stop: stop, step: step, rev: rev, done: false} +} + +impl Iterator for RangeStepInclusive + where A: CheckedAdd + PartialOrd + Clone + PartialEq +{ + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + if !self.done && ((self.rev && self.state >= self.stop) || + (!self.rev && self.state <= self.stop)) { + let result = self.state.clone(); + match self.state.checked_add(&self.step) { + Some(x) => self.state = x, + None => self.done = true + } + Some(result) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use std::usize; + use std::ops::{Add, Mul}; + use std::cmp::Ordering; + use {One, ToPrimitive}; + + #[test] + fn test_range() { + /// A mock type to check Range when ToPrimitive returns None + struct Foo; + + impl ToPrimitive for Foo { + fn to_i64(&self) -> Option { None } + fn to_u64(&self) -> Option { None } + } + + impl Add for Foo { + type Output = Foo; + + fn add(self, _: Foo) -> Foo { + Foo + } + } + + impl PartialEq for Foo { + fn eq(&self, _: &Foo) -> bool { + true + } + } + + impl PartialOrd for Foo { + fn partial_cmp(&self, _: &Foo) -> Option { + None + } + } + + impl Clone for Foo { + fn clone(&self) -> Foo { + Foo + } + } + + impl Mul for Foo { + type Output = Foo; + + fn mul(self, _: Foo) -> Foo { + Foo + } + } + + impl One for Foo { + fn one() -> Foo { + Foo + } + } + + assert!(super::range(0, 5).collect::>() == vec![0, 1, 2, 3, 4]); + assert!(super::range(-10, -1).collect::>() == + vec![-10, -9, -8, -7, -6, -5, -4, -3, -2]); + assert!(super::range(0, 5).rev().collect::>() == vec![4, 3, 2, 1, 0]); + assert_eq!(super::range(200, -5).count(), 0); + assert_eq!(super::range(200, -5).rev().count(), 0); + assert_eq!(super::range(200, 200).count(), 0); + assert_eq!(super::range(200, 200).rev().count(), 0); + + assert_eq!(super::range(0, 100).size_hint(), (100, Some(100))); + // this test is only meaningful when sizeof usize < sizeof u64 + assert_eq!(super::range(usize::MAX - 1, usize::MAX).size_hint(), (1, Some(1))); + assert_eq!(super::range(-10, -1).size_hint(), (9, Some(9))); + } + + #[test] + fn test_range_inclusive() { + assert!(super::range_inclusive(0, 5).collect::>() == + vec![0, 1, 2, 3, 4, 5]); + assert!(super::range_inclusive(0, 5).rev().collect::>() == + vec![5, 4, 3, 2, 1, 0]); + assert_eq!(super::range_inclusive(200, -5).count(), 0); + assert_eq!(super::range_inclusive(200, -5).rev().count(), 0); + assert!(super::range_inclusive(200, 200).collect::>() == vec![200]); + assert!(super::range_inclusive(200, 200).rev().collect::>() == vec![200]); + } + + #[test] + fn test_range_step() { + assert!(super::range_step(0, 20, 5).collect::>() == + vec![0, 5, 10, 15]); + assert!(super::range_step(20, 0, -5).collect::>() == + vec![20, 15, 10, 5]); + assert!(super::range_step(20, 0, -6).collect::>() == + vec![20, 14, 8, 2]); + assert!(super::range_step(200u8, 255, 50).collect::>() == + vec![200u8, 250]); + assert!(super::range_step(200, -5, 1).collect::>() == vec![]); + assert!(super::range_step(200, 200, 1).collect::>() == vec![]); + } + + #[test] + fn test_range_step_inclusive() { + assert!(super::range_step_inclusive(0, 20, 5).collect::>() == + vec![0, 5, 10, 15, 20]); + assert!(super::range_step_inclusive(20, 0, -5).collect::>() == + vec![20, 15, 10, 5, 0]); + assert!(super::range_step_inclusive(20, 0, -6).collect::>() == + vec![20, 14, 8, 2]); + assert!(super::range_step_inclusive(200u8, 255, 50).collect::>() == + vec![200u8, 250]); + assert!(super::range_step_inclusive(200, -5, 1).collect::>() == + vec![]); + assert!(super::range_step_inclusive(200, 200, 1).collect::>() == + vec![200]); + } +} diff --git a/src/lib.rs b/src/lib.rs index 36723ee..4ba0ead 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,14 +57,15 @@ html_root_url = "http://rust-num.github.io/num/", html_playground_url = "http://play.rust-lang.org/")] -extern crate num_traits; -extern crate num_integer; -#[cfg(feature = "complex")] -extern crate num_complex; +pub extern crate num_traits; +pub extern crate num_integer; +pub extern crate num_iter; +#[cfg(feature = "num-complex")] +pub extern crate num_complex; #[cfg(feature = "num-bigint")] -extern crate num_bigint; +pub extern crate num_bigint; #[cfg(feature = "num-rational")] -extern crate num_rational; +pub extern crate num_rational; #[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; @@ -84,7 +85,7 @@ pub use bigint::{BigInt, BigUint}; pub use rational::Rational; #[cfg(all(feature = "num-rational", feature="num-bigint"))] pub use rational::BigRational; -#[cfg(feature = "complex")] +#[cfg(feature = "num-complex")] pub use complex::Complex; pub use integer::Integer; pub use iter::{range, range_inclusive, range_step, range_step_inclusive}; @@ -99,8 +100,8 @@ use std::ops::{Mul}; #[cfg(feature = "num-bigint")] pub use num_bigint as bigint; pub use num_complex as complex; -pub use num_integer as integers; -pub mod iter; +pub use num_integer as integer; +pub use num_iter as iter; pub use num_traits as traits; #[cfg(feature = "num-rational")] pub use num_rational as rational; diff --git a/traits/src/lib.rs b/traits/src/lib.rs index c0dce24..869b92f 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -21,13 +21,13 @@ pub use sign::{Signed, Unsigned}; pub use int::PrimInt; pub use cast::*; -mod identities; -mod sign; -mod ops; -mod bounds; -mod float; -mod int; -mod cast; +pub mod identities; +pub mod sign; +pub mod ops; +pub mod bounds; +pub mod float; +pub mod int; +pub mod cast; /// The base trait for numeric types pub trait Num: PartialEq + Zero + One