167 lines
3.1 KiB
CoffeeScript
167 lines
3.1 KiB
CoffeeScript
### cos =====================================================================
|
|
|
|
Tags
|
|
----
|
|
scripting, JS, internal, treenode, general concept
|
|
|
|
Parameters
|
|
----------
|
|
x
|
|
|
|
General description
|
|
-------------------
|
|
Returns the cosine of x.
|
|
|
|
###
|
|
|
|
|
|
Eval_cos = ->
|
|
push(cadr(p1))
|
|
Eval()
|
|
cosine()
|
|
|
|
cosine = ->
|
|
save()
|
|
p1 = pop()
|
|
if (car(p1) == symbol(ADD))
|
|
cosine_of_angle_sum()
|
|
else
|
|
cosine_of_angle()
|
|
restore()
|
|
|
|
# Use angle sum formula for special angles.
|
|
|
|
#define A p3
|
|
#define B p4
|
|
|
|
cosine_of_angle_sum = ->
|
|
p2 = cdr(p1)
|
|
while (iscons(p2))
|
|
p4 = car(p2); # p4 is B
|
|
if (isnpi(p4)) # p4 is B
|
|
push(p1)
|
|
push(p4); # p4 is B
|
|
subtract()
|
|
p3 = pop(); # p3 is A
|
|
push(p3); # p3 is A
|
|
cosine()
|
|
push(p4); # p4 is B
|
|
cosine()
|
|
multiply()
|
|
push(p3); # p3 is A
|
|
sine()
|
|
push(p4); # p4 is B
|
|
sine()
|
|
multiply()
|
|
subtract()
|
|
return
|
|
p2 = cdr(p2)
|
|
cosine_of_angle()
|
|
|
|
cosine_of_angle = ->
|
|
|
|
if (car(p1) == symbol(ARCCOS))
|
|
push(cadr(p1))
|
|
return
|
|
|
|
if (isdouble(p1))
|
|
d = Math.cos(p1.d)
|
|
if (Math.abs(d) < 1e-10)
|
|
d = 0.0
|
|
push_double(d)
|
|
return
|
|
|
|
# cosine function is symmetric, cos(-x) = cos(x)
|
|
|
|
if (isnegative(p1))
|
|
push(p1)
|
|
negate()
|
|
p1 = pop()
|
|
|
|
# cos(arctan(x)) = 1 / sqrt(1 + x^2)
|
|
|
|
# see p. 173 of the CRC Handbook of Mathematical Sciences
|
|
|
|
if (car(p1) == symbol(ARCTAN))
|
|
push_integer(1)
|
|
push(cadr(p1))
|
|
push_integer(2)
|
|
power()
|
|
add()
|
|
push_rational(-1, 2)
|
|
power()
|
|
return
|
|
|
|
# multiply by 180/pi to go from radians to degrees.
|
|
# we go from radians to degrees because it's much
|
|
# easier to calculate symbolic results of most (not all) "classic"
|
|
# angles (e.g. 30,45,60...) if we calculate the degrees
|
|
# and the we do a switch on that.
|
|
# Alternatively, we could look at the fraction of pi
|
|
# (e.g. 60 degrees is 1/3 pi) but that's more
|
|
# convoluted as we'd need to look at both numerator and
|
|
# denominator.
|
|
|
|
push(p1)
|
|
push_integer(180)
|
|
multiply()
|
|
|
|
if evaluatingAsFloats
|
|
push_double(Math.PI)
|
|
else
|
|
push_symbol(PI)
|
|
|
|
divide()
|
|
|
|
n = pop_integer()
|
|
|
|
# most "good" (i.e. compact) trigonometric results
|
|
# happen for a round number of degrees. There are some exceptions
|
|
# though, e.g. 22.5 degrees, which we don't capture here.
|
|
if (n < 0 || isNaN(n))
|
|
push(symbol(COS))
|
|
push(p1)
|
|
list(2)
|
|
return
|
|
|
|
switch (n % 360)
|
|
when 90, 270
|
|
push_integer(0)
|
|
when 60, 300
|
|
push_rational(1, 2)
|
|
when 120, 240
|
|
push_rational(-1, 2)
|
|
when 45, 315
|
|
push_rational(1, 2)
|
|
push_integer(2)
|
|
push_rational(1, 2)
|
|
power()
|
|
multiply()
|
|
when 135, 225
|
|
push_rational(-1, 2)
|
|
push_integer(2)
|
|
push_rational(1, 2)
|
|
power()
|
|
multiply()
|
|
when 30, 330
|
|
push_rational(1, 2)
|
|
push_integer(3)
|
|
push_rational(1, 2)
|
|
power()
|
|
multiply()
|
|
when 150, 210
|
|
push_rational(-1, 2)
|
|
push_integer(3)
|
|
push_rational(1, 2)
|
|
power()
|
|
multiply()
|
|
when 0
|
|
push_integer(1)
|
|
when 180
|
|
push_integer(-1)
|
|
else
|
|
push(symbol(COS))
|
|
push(p1)
|
|
list(2)
|
|
|