Graphing expressions

Assignment Overview

In this assignment, you will create a program for graphing simple functions. You will make heavy use of interfaces, both your own interfaces and ones provided by the Go standard library. There are two main components:

1. An Expr (or expression) type, which represents a mathematical function of one variable, for example, x + 2, or 3 * x * (x + 7). An expression can Eval itself on a particular value of x, returning a number, and it can String itself (producing a string like “x + 2”).

2. A Graph struct that implements the image.Image interface. A Graph holds an Expr and some settings, like how big the graph should be and how its axes are arranged.

Instructions

1The expression interface

Declare an interface for expressions, Expr. An expression should be able to String() string itself, and Eval(x float64) float64 its value at a certain float64 value of x.

2The concrete expression types

Implement each of the following expression types as you please:

• Constant, an expression representing a constant number, like 2.3 or -50. This should Eval to itself, no matter what x is. The String function should just return a String version of this number. (Use fmt.Sprintf.)

• Negative, an expression representing the negation of another expression. (You must, of course, store that other expression.) This should print out as -BLAH, where BLAH is whatever the subexpression prints out as. It should evaluate to the negative of whatever the other expression evaluates to.

• BinaryOperation, an expression consisting of two sub-expressions and a binary operation, either +, -, *, or /. This should print as (e1 op e2), where e1 and e2 are the string versions of the two sub-expressions, and op is the operation.

• Variable, which represents a variable. (You might choose to implement this as type Variable string, where the string is the name of the variable, like “x” or “y”. Just note that your program only supports one variable; you can call it x or y or whatever, but it’s all the same thing.) It evaluates to whatever x is passed into the Eval method; it prints out as x (or whatever the variable name is).

3The Graph type

Write a struct type, Graph, that stores an expression, and some settings: a height and width in pixels, as well as a MinX, MaxX, MinY, and MaxY for the “axes” of the graph. Implement the three methods required for the Image interface:

1. ColorModel() color.Model–you can just return color.GrayModel.

2. Bounds() Rectangel–use image.Rect(0, 0, w, h) to return the width and height of your image in pixels.

3. At(x, y int) color.Color–the hard one. Given an (x, y) coordinate, decide what color to paint that pixel of the image. To do this, you need to first figure out what “real value” x corresponds to. If x is the 5th pixel in a 100-pixel image, and the axis goes from MinX = 0 to MaxX = 20, then x = 5 really corresponds to x = 1, 1/20th of the way across the axis. Once you have this, you need to evaluate your Expr at this point. This will give you a y value. Calculate what pixel that y value would land on, based on the y axis’s scale. If that pixel number is the y value passed in to you, return black (color.Gray{0}). Otherwise, return white (color.Gray{255}).

4Main function

In your main function, create a few expressions, and save them to files using code like the following:

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func main() { var e1 Expr = ... var e2 Expr = ... var graph1 = Graph{e1, 100, 100, ..., ..., ..., ...} var graph2 = Graph{e2, 100, 100, ..., ..., ..., ...} f1, err := os.Create("graph1.png") if err != nil { panic("Error opening graph1.png") } png.Encode(f1, graph1) f1.Close() f2, err := os.Create("graph2.png") if err != nil { panic("Error opening graph2.png") } png.Encode(f2, graph2) f2.Close() }