1#![cfg(feature = "std")]
9use num_traits::Float;
10
11pub struct Logspace<F>
15{
16 sign: F,
17 base: F,
18 start: F,
19 step: F,
20 index: usize,
21 len: usize,
22}
23
24impl<F> Iterator for Logspace<F>
25where F: Float
26{
27 type Item = F;
28
29 #[inline]
30 fn next(&mut self) -> Option<F>
31 {
32 if self.index >= self.len {
33 None
34 } else {
35 let i = self.index;
37 self.index += 1;
38 let exponent = self.start + self.step * F::from(i).unwrap();
39 Some(self.sign * self.base.powf(exponent))
40 }
41 }
42
43 #[inline]
44 fn size_hint(&self) -> (usize, Option<usize>)
45 {
46 let n = self.len - self.index;
47 (n, Some(n))
48 }
49}
50
51impl<F> DoubleEndedIterator for Logspace<F>
52where F: Float
53{
54 #[inline]
55 fn next_back(&mut self) -> Option<F>
56 {
57 if self.index >= self.len {
58 None
59 } else {
60 self.len -= 1;
62 let i = self.len;
63 let exponent = self.start + self.step * F::from(i).unwrap();
64 Some(self.sign * self.base.powf(exponent))
65 }
66 }
67}
68
69impl<F> ExactSizeIterator for Logspace<F> where Logspace<F>: Iterator {}
70
71#[inline]
82pub fn logspace<F>(base: F, a: F, b: F, n: usize) -> Logspace<F>
83where F: Float
84{
85 let step = if n > 1 {
86 let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail.");
87 (b - a) / num_steps
88 } else {
89 F::zero()
90 };
91 Logspace {
92 sign: base.signum(),
93 base: base.abs(),
94 start: a,
95 step,
96 index: 0,
97 len: n,
98 }
99}
100
101#[cfg(test)]
102mod tests
103{
104 use super::logspace;
105
106 #[test]
107 #[cfg(feature = "approx")]
108 fn valid()
109 {
110 use crate::{arr1, Array1};
111 use approx::assert_abs_diff_eq;
112
113 let array: Array1<_> = logspace(10.0, 0.0, 3.0, 4).collect();
114 assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3]));
115
116 let array: Array1<_> = logspace(10.0, 3.0, 0.0, 4).collect();
117 assert_abs_diff_eq!(array, arr1(&[1e3, 1e2, 1e1, 1e0]));
118
119 let array: Array1<_> = logspace(-10.0, 3.0, 0.0, 4).collect();
120 assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0]));
121
122 let array: Array1<_> = logspace(-10.0, 0.0, 3.0, 4).collect();
123 assert_abs_diff_eq!(array, arr1(&[-1e0, -1e1, -1e2, -1e3]));
124 }
125
126 #[test]
127 fn iter_forward()
128 {
129 let mut iter = logspace(10.0f64, 0.0, 3.0, 4);
130
131 assert!(iter.size_hint() == (4, Some(4)));
132
133 assert!((iter.next().unwrap() - 1e0).abs() < 1e-5);
134 assert!((iter.next().unwrap() - 1e1).abs() < 1e-5);
135 assert!((iter.next().unwrap() - 1e2).abs() < 1e-5);
136 assert!((iter.next().unwrap() - 1e3).abs() < 1e-5);
137 assert!(iter.next().is_none());
138
139 assert!(iter.size_hint() == (0, Some(0)));
140 }
141
142 #[test]
143 fn iter_backward()
144 {
145 let mut iter = logspace(10.0f64, 0.0, 3.0, 4);
146
147 assert!(iter.size_hint() == (4, Some(4)));
148
149 assert!((iter.next_back().unwrap() - 1e3).abs() < 1e-5);
150 assert!((iter.next_back().unwrap() - 1e2).abs() < 1e-5);
151 assert!((iter.next_back().unwrap() - 1e1).abs() < 1e-5);
152 assert!((iter.next_back().unwrap() - 1e0).abs() < 1e-5);
153 assert!(iter.next_back().is_none());
154
155 assert!(iter.size_hint() == (0, Some(0)));
156 }
157}