1#pragma once
2
3#include <DataTypes/DataTypesNumber.h>
4#include <Columns/ColumnsNumber.h>
5#include <Common/FieldVisitors.h>
6#include <IO/ReadHelpers.h>
7#include <IO/WriteHelpers.h>
8#include <AggregateFunctions/Helpers.h>
9#include <AggregateFunctions/IAggregateFunction.h>
10#include <Common/assert_cast.h>
11
12
13namespace DB
14{
15
16namespace ErrorCodes
17{
18 extern const int BAD_ARGUMENTS;
19}
20
21/** Tracks the leftmost and rightmost (x, y) data points.
22 */
23struct AggregateFunctionBoundingRatioData
24{
25 struct Point
26 {
27 Float64 x;
28 Float64 y;
29 };
30
31 bool empty = true;
32 Point left;
33 Point right;
34
35 void add(Float64 x, Float64 y)
36 {
37 Point point{x, y};
38
39 if (empty)
40 {
41 left = point;
42 right = point;
43 empty = false;
44 }
45 else if (point.x < left.x)
46 {
47 left = point;
48 }
49 else if (point.x > right.x)
50 {
51 right = point;
52 }
53 }
54
55 void merge(const AggregateFunctionBoundingRatioData & other)
56 {
57 if (empty)
58 {
59 *this = other;
60 }
61 else
62 {
63 if (other.left.x < left.x)
64 left = other.left;
65 if (other.right.x > right.x)
66 right = other.right;
67 }
68 }
69
70 void serialize(WriteBuffer & buf) const
71 {
72 writeBinary(empty, buf);
73
74 if (!empty)
75 {
76 writePODBinary(left, buf);
77 writePODBinary(right, buf);
78 }
79 }
80
81 void deserialize(ReadBuffer & buf)
82 {
83 readBinary(empty, buf);
84
85 if (!empty)
86 {
87 readPODBinary(left, buf);
88 readPODBinary(right, buf);
89 }
90 }
91};
92
93
94class AggregateFunctionBoundingRatio final : public IAggregateFunctionDataHelper<AggregateFunctionBoundingRatioData, AggregateFunctionBoundingRatio>
95{
96private:
97 /** Calculates the slope of a line between leftmost and rightmost data points.
98 * (y2 - y1) / (x2 - x1)
99 */
100 Float64 NO_SANITIZE_UNDEFINED getBoundingRatio(const AggregateFunctionBoundingRatioData & data) const
101 {
102 if (data.empty)
103 return std::numeric_limits<Float64>::quiet_NaN();
104
105 return (data.right.y - data.left.y) / (data.right.x - data.left.x);
106 }
107
108public:
109 String getName() const override
110 {
111 return "boundingRatio";
112 }
113
114 AggregateFunctionBoundingRatio(const DataTypes & arguments)
115 : IAggregateFunctionDataHelper<AggregateFunctionBoundingRatioData, AggregateFunctionBoundingRatio>(arguments, {})
116 {
117 const auto x_arg = arguments.at(0).get();
118 const auto y_arg = arguments.at(0).get();
119
120 if (!x_arg->isValueRepresentedByNumber() || !y_arg->isValueRepresentedByNumber())
121 throw Exception("Illegal types of arguments of aggregate function " + getName() + ", must have number representation.",
122 ErrorCodes::BAD_ARGUMENTS);
123 }
124
125 DataTypePtr getReturnType() const override
126 {
127 return std::make_shared<DataTypeFloat64>();
128 }
129
130 void add(AggregateDataPtr place, const IColumn ** columns, const size_t row_num, Arena *) const override
131 {
132 /// NOTE Slightly inefficient.
133 const auto x = columns[0]->getFloat64(row_num);
134 const auto y = columns[1]->getFloat64(row_num);
135 data(place).add(x, y);
136 }
137
138 void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena *) const override
139 {
140 data(place).merge(data(rhs));
141 }
142
143 void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override
144 {
145 data(place).serialize(buf);
146 }
147
148 void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena *) const override
149 {
150 data(place).deserialize(buf);
151 }
152
153 void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override
154 {
155 assert_cast<ColumnFloat64 &>(to).getData().push_back(getBoundingRatio(data(place)));
156 }
157};
158
159}
160