-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathinferred_number.go
136 lines (117 loc) · 2.94 KB
/
inferred_number.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
package jtdinfer
import (
"math"
jtd "github.com/jsontypedef/json-typedef-go"
)
// NumType represents the type of number a number should be represented in the
// JTD.
type NumType uint8
// Available number types.
const (
NumTypeUint8 NumType = iota
NumTypeInt8
NumTypeUint16
NumTypeInt16
NumTypeUint32
NumTypeInt32
NumTypeFloat32
NumTypeFloat64
)
// IsFloat returns true if the `NumType` is a float.
func (n NumType) IsFloat() bool {
return n == NumTypeFloat32 || n == NumTypeFloat64
}
// AsRange returns the maximum and minimum value for a `NumType`.
func (n NumType) AsRange() (float64, float64) {
switch n {
case NumTypeUint8:
return 0, math.MaxUint8
case NumTypeInt8:
return math.MinInt8, math.MaxInt8
case NumTypeUint16:
return 0, math.MaxUint16
case NumTypeInt16:
return math.MinInt16, math.MaxInt16
case NumTypeUint32:
return 0, math.MaxUint32
case NumTypeInt32:
return math.MinInt32, math.MaxInt32
case NumTypeFloat32, NumTypeFloat64:
return -math.MaxFloat64, math.MaxFloat64
}
return 0, 0
}
// IntoType will convert a `NumType` to a `jtd.Type`.
func (n NumType) IntoType() jtd.Type {
switch n {
case NumTypeUint8:
return jtd.TypeUint8
case NumTypeInt8:
return jtd.TypeInt8
case NumTypeUint16:
return jtd.TypeUint16
case NumTypeInt16:
return jtd.TypeInt16
case NumTypeUint32:
return jtd.TypeUint32
case NumTypeInt32:
return jtd.TypeInt32
case NumTypeFloat32:
return jtd.TypeFloat32
case NumTypeFloat64:
return jtd.TypeFloat64
}
return jtd.TypeUint8
}
// InferredNumber represents the state for a column that is a number. It holds
// the seen maximum and minimum value together with information about if all
// seen numbers are integers.
type InferredNumber struct {
Min float64
Max float64
IsInteger bool
}
// NewNumber will return a new `InferredNumber`.
func NewNumber() *InferredNumber {
return &InferredNumber{
IsInteger: true,
}
}
// Infer will infer a value, updating the state for the `InferredNumber`.
func (i *InferredNumber) Infer(n float64) *InferredNumber {
return &InferredNumber{
Min: math.Min(i.Min, n),
Max: math.Max(i.Max, n),
IsInteger: i.IsInteger && float64(int(n)) == n,
}
}
// IntoType will convert an `InferredNumber` to a `jtd.Type`.
func (i *InferredNumber) IntoType(defaultType NumType) jtd.Type {
if i.ContainedBy(defaultType) {
return defaultType.IntoType()
}
numTypes := []NumType{
NumTypeUint8,
NumTypeInt8,
NumTypeUint16,
NumTypeInt16,
NumTypeUint32,
NumTypeInt32,
}
for _, v := range numTypes {
if i.ContainedBy(v) {
return v.IntoType()
}
}
return jtd.TypeFloat64
}
// ContainedBy checks if an inferred number column can be contained within the
// passed `NumType`, meaning it is above the minimum and below the maximum value
// for the number type.
func (i *InferredNumber) ContainedBy(nt NumType) bool {
if !i.IsInteger && !nt.IsFloat() {
return false
}
min, max := nt.AsRange()
return min <= i.Min && max >= i.Max
}