diff --git a/Programming Languages/Part C/.vscode/launch.json b/Programming Languages/Part C/.vscode/launch.json index 9cda636..8185da6 100644 --- a/Programming Languages/Part C/.vscode/launch.json +++ b/Programming Languages/Part C/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "Debug Local File", "type": "Ruby", "request": "launch", - "cwd": "${workspaceRoot}", + "cwd": "${fileDirname}", "program": "${file}" }, { diff --git a/Programming Languages/Part C/Week 2/hw7.rb b/Programming Languages/Part C/Week 2/hw7.rb new file mode 100644 index 0000000..d90a8a7 --- /dev/null +++ b/Programming Languages/Part C/Week 2/hw7.rb @@ -0,0 +1,367 @@ +# University of Washington, Programming Languages, Homework 7, hw7.rb +# (See also ML code) + +# a little language for 2D geometry objects + +# each subclass of GeometryExpression, including subclasses of GeometryValue, +# needs to respond to messages preprocess_prog and eval_prog +# +# each subclass of GeometryValue additionally needs: +# * shift +# * intersect, which uses the double-dispatch pattern +# * intersectPoint, intersectLine, and intersectVerticalLine for +# for being called by intersect of appropriate clases and doing +# the correct intersection calculuation +# * (We would need intersectNoPoints and intersectLineSegment, but these +# are provided by GeometryValue and should not be overridden.) +# * intersectWithSegmentAsLineResult, which is used by +# intersectLineSegment as described in the assignment +# +# you can define other helper methods, but will not find much need to + +# Note: geometry objects should be immutable: assign to fields only during +# object construction + +# Note: For eval_prog, represent environments as arrays of 2-element arrays +# as described in the assignment + +class GeometryExpression + # do *not* change this class definition + Epsilon = 0.00001 +end + +class GeometryValue + # do *not* change methods in this class definition + # you can add methods if you wish + + private + + # some helper methods that may be generally useful + def real_close(r1, r2) + (r1 - r2).abs < GeometryExpression::Epsilon + end + + def real_close_point(x1, y1, x2, y2) + real_close(x1, x2) && real_close(y1, y2) + end + + # two_points_to_line could return a Line or a VerticalLine + def two_points_to_line(x1, y1, x2, y2) + if real_close(x1, x2) + VerticalLine.new x1 + else + m = (y2 - y1).to_f / (x2 - x1) + b = y1 - m * x1 + Line.new(m, b) + end + end + + public + + # we put this in this class so all subclasses can inherit it: + # the intersection of self with a NoPoints is a NoPoints object + def intersectNoPoints(np) + np # could also have NoPoints.new here instead + end + + def preprocess_prog + self # no pre-processing to do here + end + + def eval_prog(_env) + self # all values evaluate to self + end + + # we put this in this class so all subclasses can inhert it: + # the intersection of self with a LineSegment is computed by + # first intersecting with the line containing the segment and then + # calling the result's intersectWithSegmentAsLineResult with the segment + def intersectLineSegment(seg) + line_result = intersect(two_points_to_line(seg.x1, seg.y1, seg.x2, seg.y2)) + line_result.intersectWithSegmentAsLineResult seg + end +end + +class NoPoints < GeometryValue + # do *not* change this class definition: everything is done for you + # (although this is the easiest class, it shows what methods every subclass + # of geometry values needs) + # However, you *may* move methods from here to a superclass if you wish to + + # Note: no initialize method only because there is nothing it needs to do + + def shift(_dx, _dy) + self # shifting no-points is no-points + end + + def intersect(other) + other.intersectNoPoints self # will be NoPoints but follow double-dispatch + end + + def intersectPoint(_p) + self # intersection with point and no-points is no-points + end + + def intersectLine(_line) + self # intersection with line and no-points is no-points + end + + def intersectVerticalLine(_vline) + self # intersection with line and no-points is no-points + end + + # if self is the intersection of (1) some shape s and (2) + # the line containing seg, then we return the intersection of the + # shape s and the seg. seg is an instance of LineSegment + def intersectWithSegmentAsLineResult(_seg) + self + end +end + +class Point < GeometryValue + # *add* methods to this class -- do *not* change given code and do not + # override any methods + + # Note: You may want a private helper method like the local + # helper function inbetween in the ML code + attr_reader :x, :y + def initialize(x, y) + @x = x + @y = y + end + + def eval_prog(_env) + self # all values evaluate to self + end + + def shift(dx, dy) + Point.new(x + dx, y + dy) + end + + def intersect(other) + other.intersectPoint self + end + + def intersectPoint(_p) + if _p.x == x and _p.y == y + _p + else + NoPoints.new + end + end + + def intersectLine(_line) + if y==_line.m * x + _line.b + self + else + NoPoints.new + end + end + + def intersectVerticalLine(_vline) + if x==_vline.x + self + else + NoPoints.new + end + + def intersectWithSegmentAsLineResult(ls) + + end + end + + # if self is the intersection of (1) some shape s and (2) + # the line containing seg, then we return the intersection of the + # shape s and the seg. seg is an instance of LineSegment + def intersectWithSegmentAsLineResult(_seg) + self + end +end + +class Line < GeometryValue + # *add* methods to this class -- do *not* change given code and do not + # override any methods + attr_reader :m, :b + def initialize(m, b) + @m = m + @b = b + end + + def shift(dx, dy) + Line.new(m, b + dy - (m * dx)) + end + + def intersect(other) + other.intersectLine self + end + + def intersectPoint(_p) + _p.intersectLine self + end + + def intersectLine(_line) + if m==_line.m + if b==_line.b + self + else + NoPoints + end + else + tmpx = (_line.b - b)/(m - _line.m) + Point.new(tmpx,m*tmpx+b) + end + end + + def intersectVerticalLine(_vline) + Point(_vline.x,m*_vline.x+b) + end + def intersectWithSegmentAsLineResult(ls) + + end +end + +class VerticalLine < GeometryValue + # *add* methods to this class -- do *not* change given code and do not + # override any methods + attr_reader :x + def initialize(x) + @x = x + end + + def shift(dx, _dy) + VerticalLine.new(x + dx) + end + def intersect(other) + other.intersectVerticalLine self + end + def intersectPoint(_p) + _p.intersectVerticalLine self + end + def intersectLine(_line) + _line.intersectVerticalLine self + end + + def intersectVerticalLine(_vline) + if x==_vline.x + self + else + NoPoints.new + end + end + def intersectWithSegmentAsLineResult(ls) + + end +end + +class LineSegment < GeometryValue + # *add* methods to this class -- do *not* change given code and do not + # override any methods + # Note: This is the most difficult class. In the sample solution, + # preprocess_prog is about 15 lines long and + # intersectWithSegmentAsLineResult is about 40 lines long + attr_reader :x1, :y1, :x2, :y2 + def initialize(x1, y1, x2, y2) + @x1 = x1 + @y1 = y1 + @x2 = x2 + @y2 = y2 + end + + def preprocess_prog + if (x1 == x2) && (y1 == y2) + Point.new(x1, y1) + elsif x1 > x2 + LineSegment.new(x2, y2, x1, y1) + elsif (x1 == x2) && (y1 > y2) + LineSegment.new(x2, y2, x1, y1) + end + end + + def shift(dx, dy) + LineSegment.new(x1, y1, x2 + dx, y2 + dy) + end + + def intersect(other) + other.intersectLineSegment self + end + def intersectWithSegmentAsLineResult(ls) + + end +end + +# Note: there is no need for getter methods for the non-value classes + +class Intersect < GeometryExpression + # *add* methods to this class -- do *not* change given code and do not + # override any methods + def initialize(e1, e2) + @e1 = e1 + @e2 = e2 + end + + def preprocess_prog + e1.preprocess_prog + e2.preprocess_prog + self + end + + def eval_prog env + e1.eval_prog(env).intersect(e2.eval_prog(env)) + end +end + +class Let < GeometryExpression + # *add* methods to this class -- do *not* change given code and do not + # override any methods + # Note: Look at Var to guide how you implement Let + def initialize(s, e1, e2) + @s = s + @e1 = e1 + @e2 = e2 + end + + def preprocess_prog + self # no pre-processing to do here + end + + def eval_prog env + env.push([@s, @e1.eval_prog(env)]) + end +end + +class Var < GeometryExpression + # *add* methods to this class -- do *not* change given code and do not + # override any methods + def initialize s + @s = s + end + + def eval_prog env # remember: do not change this method + pr = env.assoc @s + raise 'undefined variable' if pr.nil? + + pr[1] + end + + def preprocess_prog + self # no pre-processing to do here + end +end + +class Shift < GeometryExpression + # *add* methods to this class -- do *not* change given code and do not + # override any methods + def initialize(dx, dy, e) + @dx = dx + @dy = dy + @e = e + end + + def preprocess_prog + self # no pre-processing to do here + end + + def eval_prog env + e.eval_prog(env).shift(dx, dy) + end +end diff --git a/Programming Languages/Part C/Week 2/hw7.sml b/Programming Languages/Part C/Week 2/hw7.sml new file mode 100644 index 0000000..16d477a --- /dev/null +++ b/Programming Languages/Part C/Week 2/hw7.sml @@ -0,0 +1,223 @@ +(* University of Washington, Programming Languages, Homework 7, hw7.sml + (See also Ruby code.) +*) + +(* Do not make changes to this code except where you see comments containing + the word CHANGE. *) + +(* expressions in a little language for 2D geometry objects + values: points, lines, vertical lines, line segments + other expressions: intersection of two expressions, lets, variables, + (shifts added by you) +*) +datatype geom_exp = + NoPoints + | Point of real * real (* represents point (x,y) *) + | Line of real * real (* represents line (slope, intercept) *) + | VerticalLine of real (* x value *) + | LineSegment of real * real * real * real (* x1,y1 to x2,y2 *) + | Intersect of geom_exp * geom_exp (* intersection expression *) + | Let of string * geom_exp * geom_exp (* let s = e1 in e2 *) + | Var of string + | Shift of real * real * geom_exp +(* CHANGE add shifts for expressions of the form Shift(deltaX, deltaY, exp *) + +exception BadProgram of string +exception Impossible of string + +(* helper functions for comparing real numbers since rounding means + we should never compare for equality *) + +val epsilon = 0.00001 + +fun real_close (r1,r2) = + (Real.abs (r1 - r2)) < epsilon + +(* notice curried *) +fun real_close_point (x1,y1) (x2,y2) = + real_close(x1,x2) andalso real_close(y1,y2) + +(* helper function to return the Line or VerticalLine containing + points (x1,y1) and (x2,y2). Actually used only when intersecting + line segments, but might be generally useful *) +fun two_points_to_line (x1,y1,x2,y2) = + if real_close(x1,x2) + then VerticalLine x1 + else + let + val m = (y2 - y1) / (x2 - x1) + val b = y1 - m * x1 + in + Line(m,b) + end + +(* helper function for interpreter: return value that is the intersection + of the arguments: 25 cases because there are 5 kinds of values, but + many cases can be combined, especially because intersection is commutative. + Do *not* call this function with non-values (e.g., shifts or lets) + *) +fun intersect (v1,v2) = + case (v1,v2) of + + (NoPoints, _) => NoPoints (* 5 cases *) + | (_, NoPoints) => NoPoints (* 4 additional cases *) + + | (Point p1, Point p2) => if real_close_point p1 p2 + then v1 + else NoPoints + + | (Point (x,y), Line (m,b)) => if real_close(y, m * x + b) + then v1 + else NoPoints + + | (Point (x1,_), VerticalLine x2) => if real_close(x1,x2) + then v1 + else NoPoints + + | (Point _, LineSegment seg) => intersect(v2,v1) + + | (Line _, Point _) => intersect(v2,v1) + + | (Line (m1,b1), Line (m2,b2)) => + if real_close(m1,m2) + then (if real_close(b1,b2) + then v1 (* same line *) + else NoPoints) (* parallel lines do not intersect *) + else + let (* one-point intersection *) + val x = (b2 - b1) / (m1 - m2) + val y = m1 * x + b1 + in + Point (x,y) + end + + | (Line (m1,b1), VerticalLine x2) => Point(x2, m1 * x2 + b1) + + | (Line _, LineSegment _) => intersect(v2,v1) + + | (VerticalLine _, Point _) => intersect(v2,v1) + | (VerticalLine _, Line _) => intersect(v2,v1) + + | (VerticalLine x1, VerticalLine x2) => + if real_close(x1,x2) + then v1 (* same line *) + else NoPoints (* parallel *) + + | (VerticalLine _, LineSegment seg) => intersect(v2,v1) + + | (LineSegment seg, _) => + (* the hard case, actually 4 cases because v2 could be a point, + line, vertical line, or line segment *) + (* First compute the intersection of (1) the line containing the segment + and (2) v2. Then use that result to compute what we need. *) + (case intersect(two_points_to_line seg, v2) of + NoPoints => NoPoints + | Point(x0,y0) => (* see if the point is within the segment bounds *) + (* assumes v1 was properly preprocessed *) + let + fun inbetween(v,end1,end2) = + (end1 - epsilon <= v andalso v <= end2 + epsilon) + orelse (end2 - epsilon <= v andalso v <= end1 + epsilon) + val (x1,y1,x2,y2) = seg + in + if inbetween(x0,x1,x2) andalso inbetween(y0,y1,y2) + then Point(x0,y0) + else NoPoints + end + | Line _ => v1 (* so segment seg is on line v2 *) + | VerticalLine _ => v1 (* so segment seg is on vertical-line v2 *) + | LineSegment seg2 => + (* the hard case in the hard case: seg and seg2 are on the same + line (or vertical line), but they could be (1) disjoint or + (2) overlapping or (3) one inside the other or (4) just touching. + And we treat vertical segments differently, so there are 4*2 cases. + *) + let + val (x1start,y1start,x1end,y1end) = seg + val (x2start,y2start,x2end,y2end) = seg2 + in + if real_close(x1start,x1end) + then (* the segments are on a vertical line *) + (* let segment a start at or below start of segment b *) + let + val ((aXstart,aYstart,aXend,aYend), + (bXstart,bYstart,bXend,bYend)) = if y1start < y2start + then (seg,seg2) + else (seg2,seg) + in + if real_close(aYend,bYstart) + then Point (aXend,aYend) (* just touching *) + else if aYend < bYstart + then NoPoints (* disjoint *) + else if aYend > bYend + then LineSegment(bXstart,bYstart,bXend,bYend) (* b inside a *) + else LineSegment(bXstart,bYstart,aXend,aYend) (* overlapping *) + end + else (* the segments are on a (non-vertical) line *) + (* let segment a start at or to the left of start of segment b *) + let + val ((aXstart,aYstart,aXend,aYend), + (bXstart,bYstart,bXend,bYend)) = if x1start < x2start + then (seg,seg2) + else (seg2,seg) + in + if real_close(aXend,bXstart) + then Point (aXend,aYend) (* just touching *) + else if aXend < bXstart + then NoPoints (* disjoint *) + else if aXend > bXend + then LineSegment(bXstart,bYstart,bXend,bYend) (* b inside a *) + else LineSegment(bXstart,bYstart,aXend,aYend) (* overlapping *) + end + end + | _ => raise Impossible "bad result from intersecting with a line") + | _ => raise Impossible "bad call to intersect: only for shape values" + +(* interpreter for our language: + * takes a geometry expression and returns a geometry value + * for simplicity we have the top-level function take an environment, + (which should be [] for the whole program + * we assume the expression e has already been "preprocessed" as described + in the homework assignment: + * line segments are not actually points (endpoints not real close) + * lines segment have left (or, if vertical, bottom) coordinate first +*) + +fun eval_prog (e,env) = + case e of + NoPoints => e (* first 5 cases are all values, so no computation *) + | Point _ => e + | Line _ => e + | VerticalLine _ => e + | LineSegment _ => e + | Var s => + (case List.find (fn (s2,v) => s=s2) env of + NONE => raise BadProgram("var not found: " ^ s) + | SOME (_,v) => v) + | Let(s,e1,e2) => eval_prog (e2, ((s, eval_prog(e1,env)) :: env)) + | Intersect(e1,e2) => intersect(eval_prog(e1,env), eval_prog(e2, env)) + | Shift(dx,dy,e1) => let + val res = eval_prog(e1,env) + in + case res of + NoPoints => NoPoints + | Point(x,y) => Point(x+dx,y+dy) + | Line(m,b) => Line(m,b+dy-(m*dx)) + | VerticalLine(x) => VerticalLine(x+dx) + | LineSegment(x1,y1,x2,y2) => LineSegment(x1,y1,x2+dx,y2+dy) + | _ => res + end +(* CHANGE: Add a case for Shift expressions *) + +(* CHANGE: Add function preprocess_prog of type geom_exp -> geom_exp *) + +fun preprocess_prog (e) = + case e of + LineSegment(x1,y1,x2,y2) => if real_close_point (x1,y1) (x2,y2) + then Point(x1,y1) + else if x1>x2 + then LineSegment(x2,y2,x1,y1) + else if (real_close (x1,x2) andalso y1>y2) + then LineSegment(x2,y2,x1,y1) + else e + | _ => e diff --git a/Programming Languages/Part C/Week 2/hw7testsprovided.rb b/Programming Languages/Part C/Week 2/hw7testsprovided.rb new file mode 100644 index 0000000..040e05d --- /dev/null +++ b/Programming Languages/Part C/Week 2/hw7testsprovided.rb @@ -0,0 +1,172 @@ +# University of Washington, Programming Languages, Homework 7, +# hw7testsprovided.rb + +require './hw7.rb' + +# Will not work completely until you implement all the classes and their methods + +# Will print only if code has errors; prints nothing if all tests pass + +# These tests do NOT cover all the various cases, especially for intersection + +#Constants for testing +ZERO = 0.0 +ONE = 1.0 +TWO = 2.0 +THREE = 3.0 +FOUR = 4.0 +FIVE = 5.0 +SIX = 6.0 +SEVEN = 7.0 +TEN = 10.0 + +#Point Tests +a = Point.new(THREE,FIVE) +if not (a.x == THREE and a.y == FIVE) + puts "Point is not initialized properly" +end +if not (a.eval_prog([]) == a) + puts "Point eval_prog should return self" +end +if not (a.preprocess_prog == a) + puts "Point preprocess_prog should return self" +end +a1 = a.shift(THREE,FIVE) +if not (a1.x == SIX and a1.y == TEN) + puts "Point shift not working properly" +end +a2 = a.intersect(Point.new(THREE,FIVE)) +if not (a2.x == THREE and a2.y == FIVE) + puts "Point intersect not working properly" +end +a3 = a.intersect(Point.new(FOUR,FIVE)) +if not (a3.is_a? NoPoints) + puts "Point intersect not working properly" +end + +#Line Tests +b = Line.new(THREE,FIVE) +if not (b.m == THREE and b.b == FIVE) + puts "Line not initialized properly" +end +if not (b.eval_prog([]) == b) + puts "Line eval_prog should return self" +end +if not (b.preprocess_prog == b) + puts "Line preprocess_prog should return self" +end + +b1 = b.shift(THREE,FIVE) +if not (b1.m == THREE and b1.b == ONE) + puts "Line shift not working properly" +end + +b2 = b.intersect(Line.new(THREE,FIVE)) +if not (((b2.is_a? Line)) and b2.m == THREE and b2.b == FIVE) + puts "Line intersect not working properly" +end +b3 = b.intersect(Line.new(THREE,FOUR)) +if not ((b3.is_a? NoPoints)) + puts "Line intersect not working properly" +end + +#VerticalLine Tests +c = VerticalLine.new(THREE) +if not (c.x == THREE) + puts "VerticalLine not initialized properly" +end + +if not (c.eval_prog([]) == c) + puts "VerticalLine eval_prog should return self" +end +if not (c.preprocess_prog == c) + puts "VerticalLine preprocess_prog should return self" +end +c1 = c.shift(THREE,FIVE) +if not (c1.x == SIX) + puts "VerticalLine shift not working properly" +end +c2 = c.intersect(VerticalLine.new(THREE)) +if not ((c2.is_a? VerticalLine) and c2.x == THREE ) + puts "VerticalLine intersect not working properly" +end +c3 = c.intersect(VerticalLine.new(FOUR)) +if not ((c3.is_a? NoPoints)) + puts "VerticalLine intersect not working properly" +end + +#LineSegment Tests +d = LineSegment.new(ONE,TWO,-THREE,-FOUR) +if not (d.eval_prog([]) == d) + puts "LineSegement eval_prog should return self" +end +d1 = LineSegment.new(ONE,TWO,ONE,TWO) +d2 = d1.preprocess_prog +if not ((d2.is_a? Point)and d2.x == ONE and d2.y == TWO) + puts "LineSegment preprocess_prog should convert to a Point" + puts "if ends of segment are real_close" +end + +d = d.preprocess_prog +if not (d.x1 == -THREE and d.y1 == -FOUR and d.x2 == ONE and d.y2 == TWO) + puts "LineSegment preprocess_prog should make x1 and y1" + puts "on the left of x2 and y2" +end + +d3 = d.shift(THREE,FIVE) +if not (d3.x1 == ZERO and d3.y1 == ONE and d3.x2 == FOUR and d3.y2 == SEVEN) + puts "LineSegment shift not working properly" +end + +d4 = d.intersect(LineSegment.new(-THREE,-FOUR,ONE,TWO)) +if not (((d4.is_a? LineSegment)) and d4.x1 == -THREE and d4.y1 == -FOUR and d4.x2 == ONE and d4.y2 == TWO) + puts "LineSegment intersect not working properly" +end +d5 = d.intersect(LineSegment.new(TWO,THREE,FOUR,FIVE)) +if not ((d5.is_a? NoPoints)) + puts "LineSegment intersect not working properly" +end + +#Intersect Tests +i = Intersect.new(LineSegment.new(-ONE,-TWO,THREE,FOUR), LineSegment.new(THREE,FOUR,-ONE,-TWO)) +i1 = i.preprocess_prog.eval_prog([]) +if not (i1.x1 == -ONE and i1.y1 == -TWO and i1.x2 == THREE and i1.y2 == FOUR) + puts "Intersect eval_prog should return the intersect between e1 and e2" +end + +#Var Tests +v = Var.new("a") +v1 = v.eval_prog([["a", Point.new(THREE,FIVE)]]) +if not ((v1.is_a? Point) and v1.x == THREE and v1.y == FIVE) + puts "Var eval_prog is not working properly" +end +if not (v.preprocess_prog == v) + puts "Var preprocess_prog should return self" +end + +#Let Tests +l = Let.new("a", LineSegment.new(-ONE,-TWO,THREE,FOUR), + Intersect.new(Var.new("a"),LineSegment.new(THREE,FOUR,-ONE,-TWO))) +l1 = l.preprocess_prog.eval_prog([]) +if not (l1.x1 == -ONE and l1.y1 == -TWO and l1.x2 == THREE and l1.y2 == FOUR) + puts "Let eval_prog should evaluate e2 after adding [s, e1] to the environment" +end + +#Let Variable Shadowing Test +l2 = Let.new("a", LineSegment.new(-ONE, -TWO, THREE, FOUR), + Let.new("b", LineSegment.new(THREE,FOUR,-ONE,-TWO), Intersect.new(Var.new("a"),Var.new("b")))) +l2 = l2.preprocess_prog.eval_prog([["a",Point.new(0,0)]]) +if not (l2.x1 == -ONE and l2.y1 == -TWO and l2.x2 == THREE and l2.y2 == FOUR) + puts "Let eval_prog should evaluate e2 after adding [s, e1] to the environment" +end + + +#Shift Tests +s = Shift.new(THREE,FIVE,LineSegment.new(-ONE,-TWO,THREE,FOUR)) +s1 = s.preprocess_prog.eval_prog([]) +if not (s1.x1 == TWO and s1.y1 == THREE and s1.x2 == SIX and s1.y2 == 9) + puts "Shift should shift e by dx and dy" +end + + + diff --git a/Programming Languages/Part C/Week 2/hw7testsprovided.sml b/Programming Languages/Part C/Week 2/hw7testsprovided.sml new file mode 100644 index 0000000..15b5187 --- /dev/null +++ b/Programming Languages/Part C/Week 2/hw7testsprovided.sml @@ -0,0 +1,61 @@ +(* University of Washington, Programming Languages, Homework 7 + hw7testsprovided.sml *) +(* Will not compile until you implement preprocess and eval_prog *) + +(* These tests do NOT cover all the various cases, especially for intersection *) + +use "hw7.sml"; + +(* Must implement preprocess_prog and Shift before running these tests *) + +fun real_equal(x,y) = Real.compare(x,y) = General.EQUAL; + +(* Preprocess tests *) +let + val Point(a,b) = preprocess_prog(LineSegment(3.2,4.1,3.2,4.1)) + val Point(c,d) = Point(3.2,4.1) +in + if real_equal(a,c) andalso real_equal(b,d) + then (print "preprocess converts a LineSegment to a Point successfully\n") + else (print "preprocess does not convert a LineSegment to a Point succesfully\n") +end; + +let + val LineSegment(a,b,c,d) = preprocess_prog (LineSegment(3.2,4.1,~3.2,~4.1)) + val LineSegment(e,f,g,h) = LineSegment(~3.2,~4.1,3.2,4.1) +in + if real_equal(a,e) andalso real_equal(b,f) andalso real_equal(c,g) andalso real_equal(d,h) + then (print "preprocess flips an improper LineSegment successfully\n") + else (print "preprocess does not flip an improper LineSegment successfully\n") +end; + +(* eval_prog tests with Shift*) +let + val Point(a,b) = (eval_prog (preprocess_prog (Shift(3.0, 4.0, Point(4.0,4.0))), [])) + val Point(c,d) = Point(7.0,8.0) +in + if real_equal(a,c) andalso real_equal(b,d) + then (print "eval_prog with empty environment worked\n") + else (print "eval_prog with empty environment is not working properly\n") +end; + +(* Using a Var *) +let + val Point(a,b) = (eval_prog (Shift(3.0,4.0,Var "a"), [("a",Point(4.0,4.0))])) + val Point(c,d) = Point(7.0,8.0) +in + if real_equal(a,c) andalso real_equal(b,d) + then (print "eval_prog with 'a' in environment is working properly\n") + else (print "eval_prog with 'a' in environment is not working properly\n") +end; + + +(* With Variable Shadowing *) +let + val Point(a,b) = (eval_prog (Shift(3.0,4.0,Var "a"), [("a",Point(4.0,4.0)),("a",Point(1.0,1.0))])) + val Point(c,d) = Point(7.0,8.0) +in + if real_equal(a,c) andalso real_equal(b,d) + then (print "eval_prog with shadowing 'a' in environment is working properly\n") + else (print "eval_prog with shadowing 'a' in environment is not working properly\n") +end;