Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Commit 85cd592

Browse files
authored
Merge pull request #482 from theodesp/feature/logarithms
Feature/logarithms
2 parents 9776bbd + b577732 commit 85cd592

File tree

3 files changed

+418
-0
lines changed

3 files changed

+418
-0
lines changed

sql/expression/function/logarithm.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package function
2+
3+
import (
4+
"math"
5+
"reflect"
6+
"fmt"
7+
8+
"gopkg.in/src-d/go-mysql-server.v0/sql/expression"
9+
"gopkg.in/src-d/go-mysql-server.v0/sql"
10+
"gopkg.in/src-d/go-errors.v1"
11+
)
12+
13+
// ErrInvalidArgumentForLogarithm is returned when an invalid argument value is passed to a
14+
// logarithm function
15+
var ErrInvalidArgumentForLogarithm = errors.NewKind("invalid argument value for logarithm: %v")
16+
17+
// LogBaseMaker returns LogBase creator functions with a specific base.
18+
func LogBaseMaker(base float64) func(e sql.Expression) sql.Expression {
19+
return func(e sql.Expression) sql.Expression {
20+
return NewLogBase(base, e)
21+
}
22+
}
23+
24+
// LogBase is a function that returns the logarithm of a value with a specific base.
25+
type LogBase struct {
26+
expression.UnaryExpression
27+
base float64
28+
}
29+
30+
// NewLogBase creates a new LogBase expression.
31+
func NewLogBase(base float64, e sql.Expression) sql.Expression {
32+
return &LogBase{UnaryExpression: expression.UnaryExpression{Child: e}, base: base}
33+
}
34+
35+
func (l *LogBase) String() string {
36+
switch l.base {
37+
case float64(math.E):
38+
return fmt.Sprintf("ln(%s)", l.Child)
39+
case float64(10):
40+
return fmt.Sprintf("log10(%s)", l.Child)
41+
case float64(2):
42+
return fmt.Sprintf("log2(%s)", l.Child)
43+
default:
44+
return fmt.Sprintf("log(%v, %s)", l.base, l.Child)
45+
}
46+
}
47+
48+
// TransformUp implements the Expression interface.
49+
func (l *LogBase) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) {
50+
child, err := l.Child.TransformUp(f)
51+
if err != nil {
52+
return nil, err
53+
}
54+
return f(NewLogBase(l.base, child))
55+
}
56+
57+
// Type returns the resultant type of the function.
58+
func (l *LogBase) Type() sql.Type {
59+
return sql.Float64
60+
}
61+
62+
// IsNullable implements the sql.Expression interface.
63+
func (l *LogBase) IsNullable() bool {
64+
return l.base == float64(1) || l.base <= float64(0) || l.Child.IsNullable()
65+
}
66+
67+
// Eval implements the Expression interface.
68+
func (l *LogBase) Eval(
69+
ctx *sql.Context,
70+
row sql.Row,
71+
) (interface{}, error) {
72+
v, err := l.Child.Eval(ctx, row)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
if v == nil {
78+
return nil, nil
79+
}
80+
81+
val, err := sql.Float64.Convert(v)
82+
if err != nil {
83+
return nil, sql.ErrInvalidType.New(reflect.TypeOf(v))
84+
}
85+
return computeLog(val.(float64), l.base)
86+
}
87+
88+
// Log is a function that returns the natural logarithm of a value.
89+
type Log struct {
90+
expression.BinaryExpression
91+
}
92+
93+
// NewLn creates a new Log expression.
94+
func NewLog(args ...sql.Expression) (sql.Expression, error) {
95+
argLen := len(args)
96+
if argLen == 0 || argLen > 2 {
97+
return nil, sql.ErrInvalidArgumentNumber.New("1 or 2", argLen)
98+
}
99+
100+
if argLen == 1 {
101+
return &Log{expression.BinaryExpression{Left: expression.NewLiteral(math.E, sql.Float64), Right: args[0]}}, nil
102+
} else {
103+
return &Log{expression.BinaryExpression{Left: args[0], Right: args[1]}}, nil
104+
}
105+
}
106+
107+
func (l *Log) String() string {
108+
return fmt.Sprintf("log(%s, %s)", l.Left, l.Right)
109+
}
110+
111+
// TransformUp implements the Expression interface.
112+
func (l *Log) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) {
113+
var args = make([]sql.Expression, 2)
114+
arg, err := l.Left.TransformUp(f)
115+
if err != nil {
116+
return nil, err
117+
}
118+
args[0] = arg
119+
120+
arg, err = l.Right.TransformUp(f)
121+
if err != nil {
122+
return nil, err
123+
}
124+
args[1] = arg
125+
expr, err := NewLog(args...)
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
return f(expr)
131+
}
132+
133+
// Children implements the Expression interface.
134+
func (l *Log) Children() []sql.Expression {
135+
return []sql.Expression{l.Left, l.Right}
136+
}
137+
138+
// Type returns the resultant type of the function.
139+
func (l *Log) Type() sql.Type {
140+
return sql.Float64
141+
}
142+
143+
// IsNullable implements the Expression interface.
144+
func (l *Log) IsNullable() bool {
145+
return l.Left.IsNullable() || l.Right.IsNullable()
146+
}
147+
148+
// Eval implements the Expression interface.
149+
func (l *Log) Eval(
150+
ctx *sql.Context,
151+
row sql.Row,
152+
) (interface{}, error) {
153+
left, err := l.Left.Eval(ctx, row)
154+
if err != nil {
155+
return nil, err
156+
}
157+
158+
if left == nil {
159+
return nil, nil
160+
}
161+
162+
lhs, err := sql.Float64.Convert(left)
163+
if err != nil {
164+
return nil, sql.ErrInvalidType.New(reflect.TypeOf(left))
165+
}
166+
167+
right, err := l.Right.Eval(ctx, row)
168+
if err != nil {
169+
return nil, err
170+
}
171+
172+
if right == nil {
173+
return nil, nil
174+
}
175+
176+
rhs, err := sql.Float64.Convert(right)
177+
if err != nil {
178+
return nil, sql.ErrInvalidType.New(reflect.TypeOf(right))
179+
}
180+
181+
// rhs becomes value, lhs becomes base
182+
return computeLog(rhs.(float64), lhs.(float64))
183+
}
184+
185+
func computeLog(v float64, base float64) (float64, error) {
186+
if v <= 0 {
187+
return float64(0), ErrInvalidArgumentForLogarithm.New(v)
188+
}
189+
if base == float64(1) || base <= float64(0) {
190+
return float64(0), ErrInvalidArgumentForLogarithm.New(base)
191+
}
192+
switch base {
193+
case float64(2):
194+
return math.Log2(v), nil
195+
case float64(10):
196+
return math.Log10(v), nil
197+
case math.E:
198+
return math.Log(v), nil
199+
default:
200+
// LOG(BASE,V) is equivalent to LOG(V) / LOG(BASE).
201+
return float64(math.Log(v) / math.Log(base)), nil
202+
}
203+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy