forked from mum4k/termdash
/
numbers.go
222 lines (196 loc) · 4.63 KB
/
numbers.go
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package numbers implements various numerical functions.
package numbers
import (
"image"
"math"
)
// RoundToNonZeroPlaces rounds the float up, so that it has at least the provided
// number of non-zero decimal places.
// Returns the rounded float and the number of leading decimal places that
// are zero. Returns the original float when places is zero. Negative places
// are treated as positive, so that -2 == 2.
func RoundToNonZeroPlaces(f float64, places int) (float64, int) {
if f == 0 {
return 0, 0
}
decOnly := zeroBeforeDecimal(f)
if decOnly == 0 {
return f, 0
}
nzMult := multToNonZero(decOnly)
if places == 0 {
return f, multToPlaces(nzMult)
}
plMult := placesToMult(places)
m := float64(nzMult * plMult)
return math.Ceil(f*m) / m, multToPlaces(nzMult)
}
// multToNonZero returns multiplier for the float, so that the first decimal
// place is non-zero. The float must not be zero.
func multToNonZero(f float64) int {
v := f
if v < 0 {
v *= -1
}
mult := 1
for v < 0.1 {
v *= 10
mult *= 10
}
return mult
}
// placesToMult translates the number of decimal places to a multiple of 10.
func placesToMult(places int) int {
if places < 0 {
places *= -1
}
mult := 1
for i := 0; i < places; i++ {
mult *= 10
}
return mult
}
// multToPlaces translates the multiple of 10 to a number of decimal places.
func multToPlaces(mult int) int {
places := 0
for mult > 1 {
mult /= 10
places++
}
return places
}
// zeroBeforeDecimal modifies the float so that it only has zero value before
// the decimal point.
func zeroBeforeDecimal(f float64) float64 {
var sign float64 = 1
if f < 0 {
f *= -1
sign = -1
}
floor := math.Floor(f)
return (f - floor) * sign
}
// MinMax returns the smallest and the largest value among the provided values.
// Returns (0, 0) if there are no values.
// Ignores NaN values. Allowing NaN values could lead to a corner case where all
// values can be NaN, in this case the function will return NaN as min and max.
func MinMax(values []float64) (min, max float64) {
if len(values) == 0 {
return 0, 0
}
min = math.MaxFloat64
max = -1 * math.MaxFloat64
allNaN := true
for _, v := range values {
if math.IsNaN(v) {
continue
}
allNaN = false
if v < min {
min = v
}
if v > max {
max = v
}
}
if allNaN {
return math.NaN(), math.NaN()
}
return min, max
}
// MinMaxInts returns the smallest and the largest int value among the provided
// values. Returns (0, 0) if there are no values.
func MinMaxInts(values []int) (min, max int) {
if len(values) == 0 {
return 0, 0
}
min = math.MaxInt32
max = -1 * math.MaxInt32
for _, v := range values {
if v < min {
min = v
}
if v > max {
max = v
}
}
return min, max
}
// DegreesToRadians converts degrees to the equivalent in radians.
func DegreesToRadians(degrees int) float64 {
if degrees > 360 {
degrees %= 360
}
return (float64(degrees) / 180) * math.Pi
}
// RadiansToDegrees converts radians to the equivalent in degrees.
func RadiansToDegrees(radians float64) int {
d := int(math.Round(radians * 180 / math.Pi))
if d < 0 {
d += 360
}
return d
}
// Abs returns the absolute value of x.
func Abs(x int) int {
if x < 0 {
return -x
}
return x
}
// findGCF finds the greatest common factor of two integers.
func findGCF(a, b int) int {
if a == 0 || b == 0 {
return 0
}
a = Abs(a)
b = Abs(b)
// https://en.wikipedia.org/wiki/Euclidean_algorithm
for {
rem := a % b
a = b
b = rem
if b == 0 {
break
}
}
return a
}
// SimplifyRatio simplifies the given ratio.
func SimplifyRatio(ratio image.Point) image.Point {
gcf := findGCF(ratio.X, ratio.Y)
if gcf == 0 {
return image.ZP
}
return image.Point{
X: ratio.X / gcf,
Y: ratio.Y / gcf,
}
}
// SplitByRatio splits the provided number by the specified ratio.
func SplitByRatio(n int, ratio image.Point) image.Point {
sr := SimplifyRatio(ratio)
if sr.Eq(image.ZP) {
return image.ZP
}
fn := float64(n)
sum := float64(sr.X + sr.Y)
fact := fn / sum
return image.Point{
int(math.Round(fact * float64(sr.X))),
int(math.Round(fact * float64(sr.Y))),
}
}