# Cosine function of numerical and symbolic arguments



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)

