No JavaScript → no output and no interactivity.

This literate program is

interactivein its HTML_{5}form. Edit a CoffeeScript segment to try it. You can see the generated JavaScript as you modify a CoffeeScript function by typing ‘show name’ after its definition.

*Warning!* This snippet is about calculating the distance between two points — not something you would normally worry about. It is a fundamental calculation that can be used in other algorithms such as ‘nearest neighbor’.

A point is any object that implements `x`

and `y`

properties. Here defined as a class.

`class Point`

constructor: (@x, @y) ->

draw: (ctx) -> ctx.fillRect @x, @y, 1, 1

toString: -> "(#{@x}, #{@y})"

The euclidean distance between two points is $\sqrt{{({x}_{1}-{x}_{2})}^{2}+{({y}_{1}-{y}_{2})}^{2}}$. Which can be written as $\sqrt{{a}^{2}+{b}^{2}}$ where $a={x}_{1}-{x}_{2}$ and $b={y}_{1}-{y}_{2}$.

`euclidean = (p1, p2) ->`

[a, b] = [p1?.x - p2?.x, p1?.y - p2?.y]

Math.sqrt Math.pow(a, 2) + Math.pow(b, 2)

This is a classical algorithm based on the Pythagorean theorem.

Source

Mathematically the `euclidean`

calculation is correct and in normal use it will work. However for very large and very small numbers its results are imprecise — more imprecise than they have to be. This is a consequence of the difference between numbers in mathematics and computer floating point numbers. The precision of the results are limited due to the square of the differences, the square causes overflow or underflow to occur at the square root of the machine precision. Fortunately some clever person found that expressing the calculation in another way can improve upon this situation, see Wikipedia hypot.

$\sqrt{{a}^{2}+{b}^{2}}$ = $\sqrt{{a}^{2}\cdot (1+{\left(\frac{b}{a}\right)}^{2})}$ = $\mid a\mid \sqrt{1+{\left(\frac{b}{a}\right)}^{2}}$

`hypot = (a, b) ->`

if a is 0

Math.abs b

else

Math.abs(a) * Math.sqrt 1 + Math.pow b/a, 2

hypotenuse = (p1, p2) ->

[a, b] = [p1?.x - p2?.x, p1?.y - p2?.y]

hypot a, b

As described in Wikipedia hypot, the `hypot`

function can also be used to convert to polar coordinates.

`polar = (p) ->`

[x, y] = [p.x, p.y]

r = hypot x, y

θ = Math.atan2 y, x

[r, θ]

show 'Distance from (0, 0), angle in 2π radians'

show polar new Point 1, 1

Testing with some exceptional values is a good way to check that the function behave as intended.

`show "euclidean vs hypotenuse"`

p1 = p2 = undefined

show "#{euclidean p1, p2} vs #{hypotenuse p1, p2}"

p1 = new Point 0, 0

p2 = new Point 0, 0

show "#{euclidean p1, p2} vs #{hypotenuse p1, p2}"

p1 = new Point 1e-200, 1e-200

p2 = new Point 2e-200, 2e-200

show "#{euclidean p1, p2} vs #{hypotenuse p1, p2}"

p1 = new Point 1e200, 1e200

p2 = new Point 2e200, 2e200

show "#{euclidean p1, p2} vs #{hypotenuse p1, p2}"

More comprehensive testing can be performed with QuickCheck. It is a test method where the properties of a function are described and data is generated to see if the properties hold. It is suitable for testing algorithms (in game logic and graphics) and is introduced in Smooth CoffeeScript Functions. There is also an interface reference.

You can find some support code in the solution below. It can be used with `qc.js`

to run QuickCheck with the standalone CoffeeScript compiler.

Source

This test says that the `hypot`

function is expected to return the same result as the `euclidean`

. The `euclidean`

is assumed to be correct for the usually generated arbInt numbers.

`declare 'same results for normal range numbers',`

[arbInt, arbInt, arbInt, arbInt],

(c, x1, y1, x2, y2) ->

p1 = new Point x1, y1

p2 = new Point x2, y2

d1 = euclidean p1, p2

d2 = hypotenuse p1, p2

diff = d1 - d2

epsilon = 1e-10

c.assert -epsilon < diff < epsilon

The next tests say that the `hypot`

function is expected to return different results for large numbers. There are different ways of doing so.

`arbBig = arbRange 1e155, 1e165`

declare 'different results for big range numbers',

[arbBig, arbBig, arbBig, arbBig],

(c, x1, y1, x2, y2) ->

p1 = new Point x1, y1

p2 = new Point x2, y2

d1 = euclidean p1, p2

d2 = hypotenuse p1, p2

diff = Math.abs d1 - d2

epsilon = 1e-10

c.assert diff > epsilon

`declare 'different results for large numbers',`

[arbInt, arbInt, arbInt, arbInt, arbInt, arbInt],

(c, x1, y1, e1, x2, y2, e2) ->

p1 = new Point x1*Math.pow(10, e1), y1*Math.pow(10, e1)

p2 = new Point x2*Math.pow(10, e2), y2*Math.pow(10, e2)

d1 = euclidean p1, p2

d2 = hypotenuse p1, p2

diff = Math.abs d1 - d2

c.guard diff > 1

exp = 9

c.assert e1 < -exp or e1 > exp or e2 < -exp or e2 > exp

The results will vary on each run as the data is generated. The floating point precision is implementation dependent.

`do test`

Formats CoffeeScript Markdown PDF HTML

License Creative Commons Attribution Share Alike by autotelicum © 2554/2011