-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathis.go
265 lines (222 loc) · 5.86 KB
/
is.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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
package is
import (
"fmt"
"math"
"strconv"
"strings"
"time"
"github.com/stanleynguyen/is-thirteen/internal/anagram"
)
type numberMatcher struct {
value float64
Roughly roughlyMatcher
Not invertedMatcher
DivisibleBy divisibilityMatcher
SquareOf squareMatcher
GreaterThan greaterMatcher
LessThan lessMatcher
YearOfBirth birthMatcher
}
// Number Instanitiate a matcher for number type
func Number(value float64) numberMatcher {
return numberMatcher{
value: value,
Roughly: roughlyMatcher{value},
Not: invertedMatcher{value},
DivisibleBy: divisibilityMatcher{value},
SquareOf: squareMatcher{value},
GreaterThan: greaterMatcher{value},
LessThan: lessMatcher{value},
YearOfBirth: birthMatcher{value, realClock{}},
}
}
// Thirteen Check if the number is Thirteen
func (m numberMatcher) Thirteen() bool {
return m.value == Thirteen
}
type ofWithinMatcher struct {
Of withinMatcher
}
// Within Instanitiate a matcher for number within a range around Thirtheen
func (m numberMatcher) Within(numberRange float64) ofWithinMatcher {
return ofWithinMatcher{
Of: withinMatcher{
value: m.value,
numberRange: numberRange,
},
}
}
// Plus Instanitiate a number matcher with a new value with an addition
func (m numberMatcher) Plus(number float64) numberMatcher {
return Number(m.value + number)
}
// Minus Instanitiate a number matcher with a new value with an subtraction
func (m numberMatcher) Minus(number float64) numberMatcher {
return Number(m.value - number)
}
// Times Instanitiate a number matcher with a new value with a product
func (m numberMatcher) Times(number float64) numberMatcher {
return Number(m.value * number)
}
// Divides Instanitiate a number matcher with a new value with a division
func (m numberMatcher) Divides(number float64) numberMatcher {
return Number(m.value / number)
}
type stringMatcher struct {
value string
CanSpell spellMatcher
AnagramOf anagramMatcher
Backwards backwardsMatcher
AtomicNumber atomicMatcher
}
// String Instanitiate a matcher for string type
func String(value string) stringMatcher {
return stringMatcher{
value: value,
CanSpell: spellMatcher{value},
AnagramOf: anagramMatcher{value},
Backwards: backwardsMatcher{value},
AtomicNumber: atomicMatcher{value},
}
}
// Thirteen Check whether the string is 13 in base 10 or is one of ThirteenStrings
func (m stringMatcher) Thirteen() bool {
for _, magicStr := range ThirteenStrings {
if magicStr == m.value {
return true
}
}
baseTenInt, err := strconv.ParseInt(m.value, 10, 64)
if err != nil {
return false
}
return baseTenInt == Thirteen
}
// Base Instanitiate a number matcher with a new value of base b
func (m stringMatcher) Base(b int) numberMatcher {
parsedInt, err := strconv.ParseInt(m.value, b, 64)
if err != nil {
panic(fmt.Errorf("is.String.Base: %s", err.Error()))
}
return Number(float64(parsedInt))
}
type roughlyMatcher struct {
value float64
}
// Thirteen Check if the number is roughly Thirteen
func (m roughlyMatcher) Thirteen() bool {
return math.Round(m.value) == Thirteen
}
type invertedMatcher struct {
value float64
}
// Thirteen Check if the number is not Thirteen
func (m invertedMatcher) Thirteen() bool {
return m.value != Thirteen
}
type divisibilityMatcher struct {
value float64
}
// Thirteen Check if the number is divisible by Thirteen
func (m divisibilityMatcher) Thirteen() bool {
return math.Mod(m.value, 13) == 0
}
type squareMatcher struct {
value float64
}
// Thirteen Check if the number is square of Thirteen
func (m squareMatcher) Thirteen() bool {
return m.value == Thirteen*Thirteen
}
type greaterMatcher struct {
value float64
}
// Thirteen Check if the number is greater than Thirteen
func (m greaterMatcher) Thirteen() bool {
return m.value > Thirteen
}
type lessMatcher struct {
value float64
}
// Thirteen Check if the number is less than Thirteen
func (m lessMatcher) Thirteen() bool {
return m.value < Thirteen
}
type withinMatcher struct {
value float64
numberRange float64
}
// Thirteen Check if the number is within a range of Thirteen
func (m withinMatcher) Thirteen() bool {
return m.value > (Thirteen-m.numberRange) && m.value < (Thirteen+m.numberRange)
}
type birthMatcher struct {
value float64
clock interface {
Now() time.Time
}
}
type realClock struct{}
func (realClock) Now() time.Time { return time.Now() }
// Thirteen Check if person with this year of birth is Thirteen year-old
func (m birthMatcher) Thirteen() bool {
return m.value-float64(m.clock.Now().Year()) == Thirteen
}
type spellMatcher struct {
value string
}
// Thirteen Check if a string has enough character to spells Thirteen
func (m spellMatcher) Thirteen() bool {
return hasCharacters(m.value, map[string]int{
"t": 2,
"h": 1,
"i": 1,
"r": 1,
"e": 2,
"n": 1,
})
}
type anagramMatcher struct {
value string
}
// Thirteen Check if a string is an anagram of Thirteen
func (m anagramMatcher) Thirteen() bool {
return anagram.AreAnagram(strings.ToLower(m.value), "thirteen")
}
type backwardsMatcher struct {
value string
}
// Thirteen Check if a string is thirteen spelled backwards
func (m backwardsMatcher) Thirteen() bool {
return reverse(strings.ToLower(m.value)) == "thirteen"
}
func reverse(in string) string {
var sb strings.Builder
runes := []rune(in)
for i := len(runes) - 1; 0 <= i; i-- {
sb.WriteRune(runes[i])
}
return sb.String()
}
type atomicMatcher struct {
value string
}
// Thirteen Check if the string can spell Aluminum - the 13th chemical element
func (m atomicMatcher) Thirteen() bool {
return hasCharacters(m.value, map[string]int{
"a": 1,
"l": 1,
"u": 2,
"m": 2,
"i": 1,
"n": 1,
})
}
func hasCharacters(s string, occurrenceCount map[string]int) bool {
for char, count := range occurrenceCount {
if strings.Count(strings.ToLower(s), char) < count {
return false
}
}
return true
}