Skip to content

Instantly share code, notes, and snippets.

@omaskery
Last active May 28, 2017 22:22
Show Gist options
  • Save omaskery/2e7466c0e5a02472c13e to your computer and use it in GitHub Desktop.
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).
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