aboutsummaryrefslogtreecommitdiffstats
path: root/func.go
blob: 039cb4fb4501676c93aeecfa6cd5e81a564dd271 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package main

import (
	"fmt"
	"math"
)

// parseFunction returns nil is fn is not a valid function.
func parseFunction(fn string) func(*Calculator) {
	switch fn {
	case "sin", "cos", "tan":
		return trig(fn)
	case "asin", "acos", "atan":
		return invTrig(fn)
	case "deg":
		return deg
	case "rad":
		return rad
	case "fac":
		return fac
	}
	return nil
}

// trig returns a closure that performs the trig function specified by fn.
// Panics if fn is not one of "sin", "cos" or "tan".
func trig(fn string) func(*Calculator) {
	return func(c *Calculator) {
		if len(c.stack) <= 0 {
			return
		}
		v := &c.stack[len(c.stack)-1]
		// The math package expects arguments to trig functions to be in radians.
		if c.angleMode == degrees {
			*v = degToRad(*v)
		}
		switch fn {
		case "sin":
			*v = math.Sin(*v)
		case "cos":
			*v = math.Cos(*v)
		case "tan":
			*v = math.Tan(*v)
		default:
			panic(fmt.Sprintf("invalid trig function: '%s'", fn))
		}
	}
}

// invTrig returns a closure that performs the inverse trig function specified
// by fn. Panics if fn is not one of "asin", "acos" or "atan".
func invTrig(fn string) func(*Calculator) {
	return func(c *Calculator) {
		if len(c.stack) <= 0 {
			return
		}
		v := &c.stack[len(c.stack)-1]
		switch fn {
		case "asin":
			*v = math.Asin(*v)
		case "acos":
			*v = math.Acos(*v)
		case "atan":
			*v = math.Atan(*v)
		default:
			panic(fmt.Sprintf("invalid inverse trig function: '%s'", fn))
		}
		if c.angleMode == degrees {
			*v = radToDeg(*v)
		}
	}
}

// Convert radians to degrees.
func deg(c *Calculator) {
	if n, err := c.stack.pop(); err == nil {
		c.stack.push(radToDeg(n))
	}
}

// Convert degrees to radians.
func rad(c *Calculator) {
	if n, err := c.stack.pop(); err == nil {
		c.stack.push(degToRad(n))
	}
}

// Factorial (!).
func fac(c *Calculator) {
	n, err := c.stack.pop()
	if err != nil {
		return
	}
	if !isUint(n) { // undefined on non-ints
		c.stack.push(n)
		return
	}
	c.stack.push(float64(factorial(uint(n))))
}

func degToRad(deg float64) (rad float64) {
	return deg * math.Pi / 180.0
}

func radToDeg(rad float64) (deg float64) {
	return rad * 180 / math.Pi
}

// factorial returns n! (n factorial).
func factorial(n uint) uint {
	if n == 0 { // 0! = 1
		return 1
	}
	// n! = n*(n-1)!
	for i := n - 1; i > 1; i-- {
		n *= i
	}
	return n
}

func isUint(n float64) bool {
	return float64(uint(n)) == n
}