/*
* Name: fibonacci.go
* Date: November 16, 2009
* Author: Jack Slingerland (jack.slingerland@gmail.com)
*
* Description: This program is a Fibonacci number calculator.
* It may be invoked as follows: "./8.out [goal]", where [goal]
* is the Fibonacci number that you want to computer. Currently
* the program is limited to the 46th Fibonacci number due to
* overflow, however a new implementation is in the works that
* won't have that limitation. The real purpose of this program
* is to show how Go routines work, and how communication and
* sychronization between them is handled using channels.
*/
package main
import (
"os";
"fmt";
"strconv";
)
/*
* This ADT holds all the information needed to calculate the next
* Fibonacci number in the sequence. Current is the current index
* in the series (ex, 5 = We're on the 5th number in the sequence).
* Goal is of course, the number in the sequence that we'd like to
* achieve.
*/
type fibonacci struct {
a int;
b int;
current int;
goal int;
}
func main() {
//Check the command line arguments for sanity.
goal, err := checkArgs();
if(err != "") {
fmt.Fprintf(os.Stdout, "Error: %s", err);
os.Exit(1);
}
//Special case: If the goal is 2 or less, the Fibonacci number is always
//1. This isn't really worth creating channels + go routines for.
if(goal <= 2) {
fmt.Fprintf(os.Stdout, "Fibnonacci Value %d is: %d\n", goal, 1);
} else {
//Construct two channels, the input channel acts as our communication
//between the Go routines. As you can see, it can be typed as an
//ADT(struct), which makes passing data REALLY easy between go routines.
//The final channel is for passing back the goal Fibonacci number to
//the main thread.
input := make(chan fibonacci);
final := make(chan int);
//Create a new fibonacci object and set it's values.
var x fibonacci;
x.a = 1;
x.b = 1;
x.current = 2;
x.goal = goal;
//Create a new Go routine with the addNumber function. We pass it the
//two channels that were declared earlier. This is similiar to "spawn"
//in Limbo.
go addNumber(input, final);
//The first addNumber() Go routine will block until it gets input from
//the input channel. So, we pass it our fibonacci object.
input <- x;
//Now, we block and wait for the Go routines (addNumber()) to finish up
//and send us back the final value.
number := <- final;
//Print the value out and we're done!
fmt.Fprintf(os.Stdout, "Fibnonacci Value %d is: %d\n", x.goal, number);
}
}
/*
* The addNumber() function takes two channels as parameters. One is a
* channel of fibonacci, which holds information about which number to
* calculate and when to stop, and then a channel of type int, which will
* be used to send the final goal number back to the main thread.
*/
func addNumber(input chan fibonacci, final chan int) () {
//Block here and wait for input from the previous thread
//or the main thread, depending on the situation.
x := <- input;
//Here we check to see if we have met our goal, if we
//have, we return the goal value back to the main thread
//via the final channel. Otherwise, we calculate the
//next number in the sequence.
if(x.current < x.goal) {
fib := x.a + x.b;
//This is fun because you can assign multiple values at once.
//x.a <- x.b and x.b <- fib. :)
x.a, x.b = x.b, fib;
x.current = x.current + 1;
//Make a new channel of type fibonacci so that we can communicate
//with the new Go routine that we are about to create.
output := make(chan fibonacci);
//Pass the new Go routine the newly created channel, as well as the
//final channel that was passed in from the caller.
go addNumber(output, final);
//Send the fibonacci object out on the channel.
output <- x;
} else {
//Send the goal value out on the channel back to main.
final <- x.b;
}
//Go routine dies now.
}
/*
* The checkArgs() function is a great example of returning multiple
* values. We return an integer and a string to the caller, so handling
* errors is fairly straight forward.
*/
func checkArgs() (int, string) {
//Fetch the command line arguments.
args := os.Args;
//Check the length of the arugments, return failure if that are too
//long or too short.
if((len(args) < 2) || (len(args) >= 3)) {
return -1, "Invalid number of arguments.\n";
}
//Convert the goal argument to an integer.
goal, err := strconv.Atoi(args[1]);
//Make sure the conversion went correctly, otherwise return failure.
if(err != nil) {
return -1, "Invalid argument. Argument must be an integer.\n";
}
//Since this implementation is limited, make sure the user can't go
//beyond the program's limits.
if(goal > 46) {
return -1, "This program only calculates up to the 46th Fibonacci number.\n";
}
//Check the lower bound as well.
if(goal < 1) {
return -1, "Invalid range. Number must be >= 1.\n";
}
//On success, return the goal value and an empty string indicating
//that everything is good.
return goal, "";
}