1/* -*- c-basic-offset: 2 -*- */
2/*
3 Copyright(C) 2016 Brazil
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License version 2.1 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17*/
18
19#ifdef GRN_EMBEDDED
20# define GRN_PLUGIN_FUNCTION_TAG functions_number
21#endif
22
23#include <groonga/plugin.h>
24
25#include <math.h>
26
27static grn_obj *
28func_number_classify(grn_ctx *ctx, int n_args, grn_obj **args,
29 grn_user_data *user_data)
30{
31 grn_obj *number;
32 grn_obj *interval;
33 grn_obj casted_interval;
34 grn_obj *classed_number;
35
36 if (n_args != 2) {
37 GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
38 "number_classify(): wrong number of arguments (%d for 2)",
39 n_args);
40 return NULL;
41 }
42
43 number = args[0];
44 if (!(number->header.type == GRN_BULK &&
45 grn_type_id_is_number_family(ctx, number->header.domain))) {
46 grn_obj inspected;
47
48 GRN_TEXT_INIT(&inspected, 0);
49 grn_inspect(ctx, &inspected, number);
50 GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
51 "number_classify(): the first argument must be a number: "
52 "<%.*s>",
53 (int)GRN_TEXT_LEN(&inspected),
54 GRN_TEXT_VALUE(&inspected));
55 GRN_OBJ_FIN(ctx, &inspected);
56 return NULL;
57 }
58
59 interval = args[1];
60 if (!(interval->header.type == GRN_BULK &&
61 grn_type_id_is_number_family(ctx, interval->header.domain))) {
62 grn_obj inspected;
63
64 GRN_TEXT_INIT(&inspected, 0);
65 grn_inspect(ctx, &inspected, interval);
66 GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
67 "number_classify(): the second argument must be a number: "
68 "<%.*s>",
69 (int)GRN_TEXT_LEN(&inspected),
70 GRN_TEXT_VALUE(&inspected));
71 GRN_OBJ_FIN(ctx, &inspected);
72 return NULL;
73 }
74
75 classed_number = grn_plugin_proc_alloc(ctx,
76 user_data,
77 number->header.domain,
78 0);
79 if (!classed_number) {
80 return NULL;
81 }
82
83 GRN_VALUE_FIX_SIZE_INIT(&casted_interval, 0, number->header.domain);
84 grn_obj_cast(ctx, interval, &casted_interval, GRN_FALSE);
85
86#define CLASSIFY_RAW(type, getter, setter, classifier) { \
87 type number_raw; \
88 type interval_raw; \
89 type class_raw; \
90 type classed_number_raw; \
91 \
92 number_raw = getter(number); \
93 interval_raw = getter(&casted_interval); \
94 class_raw = classifier(number_raw, interval_raw); \
95 classed_number_raw = class_raw * interval_raw; \
96 setter(ctx, classed_number, classed_number_raw); \
97 }
98
99#define CLASSIFIER_INT(number_raw, interval_raw) \
100 (number_raw) < 0 ? \
101 ((((number_raw) + 1) / (interval_raw)) - 1) : \
102 (((number_raw) / (interval_raw)))
103
104#define CLASSIFY_INT(type, getter, setter) \
105 CLASSIFY_RAW(type, getter, setter, CLASSIFIER_INT)
106
107#define CLASSIFIER_UINT(number_raw, interval_raw) \
108 ((number_raw) / (interval_raw))
109
110#define CLASSIFY_UINT(type, getter, setter) \
111 CLASSIFY_RAW(type, getter, setter, CLASSIFIER_UINT)
112
113#define CLASSIFIER_FLOAT(number_raw, interval_raw) \
114 floor((number_raw) / (interval_raw))
115
116#define CLASSIFY_FLOAT(getter, setter) \
117 CLASSIFY_RAW(double, getter, setter, CLASSIFIER_FLOAT)
118
119 switch (number->header.domain) {
120 case GRN_DB_INT8 :
121 CLASSIFY_INT(int8_t, GRN_INT8_VALUE, GRN_INT8_SET);
122 break;
123 case GRN_DB_UINT8 :
124 CLASSIFY_UINT(uint8_t, GRN_UINT8_VALUE, GRN_UINT8_SET);
125 break;
126 case GRN_DB_INT16 :
127 CLASSIFY_INT(int16_t, GRN_INT16_VALUE, GRN_INT16_SET);
128 break;
129 case GRN_DB_UINT16 :
130 CLASSIFY_UINT(uint16_t, GRN_UINT16_VALUE, GRN_UINT16_SET);
131 break;
132 case GRN_DB_INT32 :
133 CLASSIFY_INT(int32_t, GRN_INT32_VALUE, GRN_INT32_SET);
134 break;
135 case GRN_DB_UINT32 :
136 CLASSIFY_UINT(uint32_t, GRN_UINT32_VALUE, GRN_UINT32_SET);
137 break;
138 case GRN_DB_INT64 :
139 CLASSIFY_INT(int64_t, GRN_INT64_VALUE, GRN_INT64_SET);
140 break;
141 case GRN_DB_UINT64 :
142 CLASSIFY_UINT(uint64_t, GRN_UINT64_VALUE, GRN_UINT64_SET);
143 break;
144 case GRN_DB_FLOAT :
145 CLASSIFY_FLOAT(GRN_FLOAT_VALUE, GRN_FLOAT_SET);
146 break;
147 default :
148 break;
149 }
150#undef CLASSIFY_FLOAT
151#undef CLASSIFIER_FLAOT
152#undef CLASSIFY_UINT
153#undef CLASSIFIER_UINT
154#undef CLASSIFY_INT
155#undef CLASSIFIER_INT
156#undef CLASSIFY_RAW
157
158 GRN_OBJ_FIN(ctx, &casted_interval);
159
160 return classed_number;
161}
162
163grn_rc
164GRN_PLUGIN_INIT(grn_ctx *ctx)
165{
166 return ctx->rc;
167}
168
169grn_rc
170GRN_PLUGIN_REGISTER(grn_ctx *ctx)
171{
172 grn_rc rc = GRN_SUCCESS;
173
174 grn_proc_create(ctx,
175 "number_classify", -1,
176 GRN_PROC_FUNCTION,
177 func_number_classify,
178 NULL, NULL, 0, NULL);
179
180 return rc;
181}
182
183grn_rc
184GRN_PLUGIN_FIN(grn_ctx *ctx)
185{
186 return GRN_SUCCESS;
187}
188