Last active
May 28, 2017 22:22
-
-
Save omaskery/2e7466c0e5a02472c13e to your computer and use it in GitHub Desktop.
Creating terribly over-engineered solution to Euler Problem #2 just to play with Rust (which is lovely).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::iter::AdditiveIterator; | |
use std::num::Int; | |
// Context for a fibonacci sequence generator | |
struct FibonacciContext<T> where T: Int { | |
two_ago: Option<T>, // the fibonacci number two previous in the sequence | |
one_ago: Option<T>, // the last fibonacci number in the sequence | |
} | |
// The iterator object for iterating fibonacci numbers | |
struct FibonacciIterator<T> where T: Int { | |
context: FibonacciContext<T>, // the context we use to generate the next number | |
limit: Option<T>, // the optional limit telling us when to stop | |
} | |
impl<T> FibonacciContext<T> where T: Int { | |
// creates a new fibonacci context | |
fn new() -> FibonacciContext<T> { | |
FibonacciContext { | |
two_ago: None, // start of having generated | |
one_ago: None, // no values yet | |
} | |
} | |
// generate the next fibonacci value | |
fn update(&mut self) -> T { | |
// look to see how many we've generated so far | |
let (two, one) = match (self.two_ago, self.one_ago) { | |
// if we've generated two before, sum them to generate the next one | |
// and "slide" the numbers along | |
(Some(two), Some(one)) => { | |
(Some(one), Some(two + one)) | |
}, | |
// if we've only generated one before, slide a 1 into our two variables | |
(_, Some(one)) => { | |
(Some(one), Some(Int::one())) | |
}, | |
// we've not generated anything previously, slide a 1 into our first variable | |
_ => { | |
(None, Some(Int::one())) | |
}, | |
}; | |
// assign the result of our check above | |
self.two_ago = two; | |
self.one_ago = one; | |
// return the latest value generated | |
one.unwrap() | |
} | |
// ask what the last value generated was | |
fn last(&self) -> Option<T> { | |
self.one_ago | |
} | |
} | |
impl<T> FibonacciIterator<T> where T: Int { | |
// creates an infinite fibonacci number iterator | |
fn new() -> FibonacciIterator<T> { | |
FibonacciIterator { | |
context: FibonacciContext::new(), | |
limit: None, | |
} | |
} | |
// creates a fibonacci number generated that stops when | |
// it generates a value >= the limit | |
fn with_limit(limit: T) -> FibonacciIterator<T> { | |
FibonacciIterator { | |
context: FibonacciContext::new(), | |
limit: Some(limit), | |
} | |
} | |
} | |
// implement the iterator interface on the FibonacciIterator | |
impl<T> Iterator for FibonacciIterator<T> where T: Int { | |
type Item = T; | |
fn next(&mut self) -> Option<T> { | |
// update the internal context | |
let value = self.context.update(); | |
// react based on whether we have a limit to reach | |
match self.limit { | |
// if we do, stop if we've hit it, | |
// otherwise return the new value | |
Some(limit) => match value { | |
x if x >= limit => None, | |
x => Some(x), | |
}, | |
// with no limit, just return the new value | |
_ => Some(value), | |
} | |
} | |
} | |
// convenience method to generate the Nth fibonacci value | |
fn fibonacci<T>(n: T) -> T where T: Int { | |
// create a mutable context | |
let mut context = FibonacciContext::new(); | |
// update the context N times | |
for _ in range(Int::zero(), n) { | |
context.update(); | |
} | |
// return the last value it generated | |
context.last().unwrap() | |
} | |
// convenience method for creating an infinite fibonacci number iterator | |
fn gen_fibonacci<T>() -> FibonacciIterator<T> where T: Int { | |
FibonacciIterator::new() | |
} | |
// convenience method for creating a fibonacci number iterator that stops | |
// when it produces a value >= the provided limit | |
fn gen_fibonacci_upto<T>(limit: T) -> FibonacciIterator<T> where T: Int { | |
FibonacciIterator::with_limit(limit) | |
} | |
#[cfg(not(test))] | |
fn main() { | |
let answer = gen_fibonacci_upto(4000000u32) | |
.filter(|x| x % 2 == 0) | |
.sum(); | |
println!("answer to euler problem #2: {}", answer); | |
} | |
#[cfg(test)] | |
mod tests { | |
extern crate test; | |
use super::{fibonacci, gen_fibonacci, gen_fibonacci_upto}; | |
use self::test::Bencher; | |
#[test] | |
fn test_fibonacci() { | |
assert_eq!(fibonacci(1u32), 1); | |
assert_eq!(fibonacci(2u32), 1); | |
assert_eq!(fibonacci(3u32), 2); | |
assert_eq!(fibonacci(4u32), 3); | |
assert_eq!(fibonacci(5u32), 5); | |
assert_eq!(fibonacci(6u32), 8); | |
assert_eq!(fibonacci(43u32), 433494437); | |
} | |
#[test] | |
fn test_gen_fibonnaci() { | |
let output = gen_fibonacci().take(6).collect(); | |
let expected = vec![1, 1, 2, 3, 5, 8]; | |
assert_eq!(output, expected); | |
assert_eq!(gen_fibonacci::<u32>().take(43).collect::<Vec<u32>>()[42], 433494437); | |
} | |
#[test] | |
fn test_gen_fibonnaci_upto() { | |
let output = gen_fibonacci_upto(10).collect(); | |
let expected = vec![1, 1, 2, 3, 5, 8]; | |
assert_eq!(output, expected); | |
assert_eq!(gen_fibonacci_upto::<u32>(433494438).collect::<Vec<u32>>()[42], 433494437); | |
} | |
#[bench] | |
fn bench_fibonacci(b: &mut Bencher) { | |
b.iter(|| fibonacci(43u32)); | |
} | |
#[bench] | |
fn bench_gen_fibonnaci(b: &mut Bencher) { | |
b.iter(|| gen_fibonacci::<u32>().take(43).collect::<Vec<u32>>()); | |
} | |
#[bench] | |
fn bench_gen_fibonnaci_upto(b: &mut Bencher) { | |
b.iter(|| gen_fibonacci_upto(433494437).collect::<Vec<u32>>()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment