1 /*
2 * fn-erlang.c: Teletraffic functions.
3 *
4 * Authors:
5 * Arief Mulya Utama <arief_m_utama@telkomsel.co.id>
6 * <arief.utama@gmail.com>
7 * [Initial plugin]
8 *
9 * Morten Welinder <terra@gnome.org>
10 * [calculate_loggos]
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses/>.
24 */
25 #include <gnumeric-config.h>
26 #include <gnumeric.h>
27 #include <func.h>
28 #include <parse-util.h>
29 #include <cell.h>
30 #include <value.h>
31 #include <mathfunc.h>
32 #include <gnm-format.h>
33 #include <workbook.h>
34 #include <sheet.h>
35 #include <tools/goal-seek.h>
36 #include <gnm-i18n.h>
37
38 #include <goffice/goffice.h>
39 #include <gnm-plugin.h>
40
41 #include <math.h>
42 #include <string.h>
43 #include <stdlib.h>
44
45 /*
46 * comp_gos == 1 - gos
47 */
48 static gnm_float
guess_carried_traffic(gnm_float traffic,gnm_float comp_gos)49 guess_carried_traffic (gnm_float traffic, gnm_float comp_gos)
50 {
51 return traffic * comp_gos;
52 }
53
54 static gnm_float
calculate_loggos(gnm_float traffic,gnm_float circuits)55 calculate_loggos (gnm_float traffic, gnm_float circuits)
56 {
57 if (traffic < 0 || circuits < 1)
58 return gnm_nan;
59
60 return (dgamma (traffic, circuits + 1, 1, TRUE) -
61 pgamma (traffic, circuits + 1, 1, FALSE, TRUE));
62 }
63
64 static gnm_float
calculate_gos(gnm_float traffic,gnm_float circuits,gboolean comp)65 calculate_gos (gnm_float traffic, gnm_float circuits, gboolean comp)
66 {
67 gnm_float gos;
68
69 /* extra guards won't hurt, right? */
70 if (circuits < 1 || traffic < 0)
71 return -1;
72
73 if (traffic == 0)
74 gos = comp ? 1 : 0;
75 else if (circuits < 100) {
76 gnm_float cir_iter = 1;
77 gos = 1;
78 for (cir_iter = 1; cir_iter <= circuits; cir_iter++)
79 gos = (traffic * gos) / (cir_iter + (traffic * gos));
80 if (comp) gos = 1 - gos;
81 } else if (circuits / traffic < 0.9) {
82 gnm_float sum = 0, term = 1, n = circuits;
83 while (n > 1) {
84 term *= n / traffic;
85 if (term < GNM_EPSILON * sum)
86 break;
87 sum += term;
88 n--;
89 }
90 gos = comp ? sum / (1 + sum) : 1 / (1 + sum);
91 } else {
92 gnm_float loggos = calculate_loggos (traffic, circuits);
93 gos = comp ? -gnm_expm1 (loggos) : gnm_exp (loggos);
94 }
95
96 return gos;
97 }
98
99 GNM_PLUGIN_MODULE_HEADER;
100
101 /***************************************************************************/
102 static GnmFuncHelp const help_probblock[] = {
103 { GNM_FUNC_HELP_NAME, F_("PROBBLOCK:probability of blocking")},
104 { GNM_FUNC_HELP_ARG, F_("traffic:number of calls")},
105 { GNM_FUNC_HELP_ARG, F_("circuits:number of circuits")},
106 { GNM_FUNC_HELP_DESCRIPTION, F_("PROBBLOCK returns probability of blocking when @{traffic}"
107 " calls load into @{circuits} circuits.")},
108 { GNM_FUNC_HELP_NOTE, F_("@{traffic} cannot exceed @{circuits}.") },
109 { GNM_FUNC_HELP_EXAMPLES, "=PROBBLOCK(24,30)" },
110 { GNM_FUNC_HELP_SEEALSO, "OFFTRAF,DIMCIRC,OFFCAP"},
111 { GNM_FUNC_HELP_END }
112 };
113
114 static GnmValue *
gnumeric_probblock(GnmFuncEvalInfo * ei,GnmValue const * const * argv)115 gnumeric_probblock (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
116 {
117 gnm_float traffic = value_get_as_float (argv[0]);
118 gnm_float circuits = value_get_as_float (argv[1]);
119 gnm_float gos = calculate_gos (traffic, circuits, FALSE);
120
121 if (gos >= 0)
122 return value_new_float (gos);
123 else
124 return value_new_error_VALUE (ei->pos);
125 }
126
127 static GnmFuncHelp const help_offtraf[] = {
128 { GNM_FUNC_HELP_NAME, F_("OFFTRAF:predicted number of offered calls")},
129 { GNM_FUNC_HELP_ARG, F_("traffic:number of carried calls")},
130 { GNM_FUNC_HELP_ARG, F_("circuits:number of circuits")},
131 { GNM_FUNC_HELP_DESCRIPTION, F_("OFFTRAF returns the predicted number of offered calls given @{traffic} carried calls (taken from measurements) on @{circuits} circuits.")},
132 { GNM_FUNC_HELP_NOTE, F_("@{traffic} cannot exceed @{circuits}.") },
133 { GNM_FUNC_HELP_EXAMPLES, "=OFFTRAF(24,30)" },
134 { GNM_FUNC_HELP_SEEALSO, "PROBBLOCK,DIMCIRC,OFFCAP"},
135 { GNM_FUNC_HELP_END }
136 };
137
138 typedef struct {
139 gnm_float traffic, circuits;
140 } gnumeric_offtraf_t;
141
142 static GnmGoalSeekStatus
gnumeric_offtraf_f(gnm_float off_traffic,gnm_float * y,void * user_data)143 gnumeric_offtraf_f (gnm_float off_traffic, gnm_float *y, void *user_data)
144 {
145 gnumeric_offtraf_t *pudata = user_data;
146 gnm_float comp_gos = calculate_gos (off_traffic, pudata->circuits, TRUE);
147 if (comp_gos < 0)
148 return GOAL_SEEK_ERROR;
149 *y = guess_carried_traffic (off_traffic, comp_gos) - pudata->traffic;
150 return GOAL_SEEK_OK;
151 }
152
153 static GnmValue *
gnumeric_offtraf(GnmFuncEvalInfo * ei,GnmValue const * const * argv)154 gnumeric_offtraf (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
155 {
156 gnm_float traffic = value_get_as_float (argv[0]);
157 gnm_float circuits = value_get_as_float (argv[1]);
158 gnm_float traffic0;
159 GnmGoalSeekData data;
160 GnmGoalSeekStatus status;
161 gnumeric_offtraf_t udata;
162
163 if (circuits < 1 || traffic < 0)
164 return value_new_error_VALUE (ei->pos);
165
166 goal_seek_initialize (&data);
167 data.xmin = traffic;
168 data.xmax = circuits;
169 udata.circuits = circuits;
170 udata.traffic = traffic;
171 traffic0 = (data.xmin + data.xmax) / 2;
172 /* Newton search from guess. */
173 status = goal_seek_newton (&gnumeric_offtraf_f, NULL,
174 &data, &udata, traffic0);
175 if (status != GOAL_SEEK_OK) {
176 (void)goal_seek_point (&gnumeric_offtraf_f, &data, &udata, traffic);
177 (void)goal_seek_point (&gnumeric_offtraf_f, &data, &udata, circuits);
178 status = goal_seek_bisection (&gnumeric_offtraf_f, &data, &udata);
179 }
180
181 if (status == GOAL_SEEK_OK)
182 return value_new_float (data.root);
183 else
184 return value_new_error_VALUE (ei->pos);
185 }
186
187 static GnmFuncHelp const help_dimcirc[] = {
188 { GNM_FUNC_HELP_NAME, F_("DIMCIRC:number of circuits required")},
189 { GNM_FUNC_HELP_ARG, F_("traffic:number of calls")},
190 { GNM_FUNC_HELP_ARG, F_("gos:grade of service")},
191 { GNM_FUNC_HELP_DESCRIPTION, F_("DIMCIRC returns the number of circuits required given @{traffic} calls with grade of service @{gos}.")},
192 { GNM_FUNC_HELP_EXAMPLES, "=DIMCIRC(24,0.01)" },
193 { GNM_FUNC_HELP_SEEALSO, "OFFCAP,OFFTRAF,PROBBLOCK"},
194 { GNM_FUNC_HELP_END }
195 };
196
197 static GnmValue *
gnumeric_dimcirc(GnmFuncEvalInfo * ei,GnmValue const * const * argv)198 gnumeric_dimcirc (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
199 {
200 gnm_float traffic = value_get_as_float (argv[0]);
201 gnm_float des_gos = value_get_as_float (argv[1]);
202 gnm_float low, high;
203
204 if (des_gos > 1 || des_gos <= 0)
205 return value_new_error_VALUE (ei->pos);
206
207 low = high = 1;
208 while (calculate_gos (traffic, high, FALSE) > des_gos) {
209 low = high;
210 high += high;
211 }
212
213 while (high - low > 1.5) {
214 gnm_float mid = gnm_floor ((high + low) / 2 + 0.1);
215 gnm_float gos = calculate_gos (traffic, mid, FALSE);
216 if (gos > des_gos)
217 low = mid;
218 else
219 high = mid;
220 }
221
222 return value_new_float (high);
223 }
224
225 static GnmFuncHelp const help_offcap[] = {
226 { GNM_FUNC_HELP_NAME, F_("OFFCAP:traffic capacity")},
227 { GNM_FUNC_HELP_ARG, F_("circuits:number of circuits")},
228 { GNM_FUNC_HELP_ARG, F_("gos:grade of service")},
229 { GNM_FUNC_HELP_DESCRIPTION, F_("OFFCAP returns the traffic capacity given @{circuits} circuits with grade of service @{gos}.")},
230 { GNM_FUNC_HELP_EXAMPLES, "=OFFCAP(30,0.01)" },
231 { GNM_FUNC_HELP_SEEALSO, "DIMCIRC,OFFTRAF,PROBBLOCK"},
232 { GNM_FUNC_HELP_END }
233 };
234
235 typedef struct {
236 gnm_float circuits, des_gos;
237 } gnumeric_offcap_t;
238
239 static GnmGoalSeekStatus
gnumeric_offcap_f(gnm_float traffic,gnm_float * y,void * user_data)240 gnumeric_offcap_f (gnm_float traffic, gnm_float *y, void *user_data)
241 {
242 gnumeric_offcap_t *pudata = user_data;
243 gnm_float gos = calculate_gos (traffic, pudata->circuits, FALSE);
244 if (gos < 0)
245 return GOAL_SEEK_ERROR;
246 *y = gos - pudata->des_gos;
247 return GOAL_SEEK_OK;
248 }
249
250 static GnmValue *
gnumeric_offcap(GnmFuncEvalInfo * ei,GnmValue const * const * argv)251 gnumeric_offcap (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
252 {
253 gnm_float circuits = value_get_as_float (argv[0]);
254 gnm_float des_gos = value_get_as_float (argv[1]);
255 gnm_float traffic0;
256 GnmGoalSeekData data;
257 GnmGoalSeekStatus status;
258 gnumeric_offcap_t udata;
259
260 if (des_gos >= 1 || des_gos <= 0)
261 return value_new_error_VALUE (ei->pos);
262
263 goal_seek_initialize (&data);
264 data.xmin = 0;
265 data.xmax = circuits / (1 - des_gos);
266 udata.circuits = circuits;
267 udata.des_gos = des_gos;
268
269 traffic0 = data.xmax * (2 + des_gos * 10) / (3 + des_gos * 10);
270 /* Newton search from guess. */
271 status = goal_seek_newton (&gnumeric_offcap_f, NULL,
272 &data, &udata, traffic0);
273 if (status != GOAL_SEEK_OK) {
274 (void)goal_seek_point (&gnumeric_offcap_f, &data, &udata, data.xmin);
275 (void)goal_seek_point (&gnumeric_offcap_f, &data, &udata, data.xmax);
276 status = goal_seek_bisection (&gnumeric_offcap_f, &data, &udata);
277 }
278
279 if (status == GOAL_SEEK_OK)
280 return value_new_float (data.root);
281 else
282 return value_new_error_VALUE (ei->pos);
283 }
284
285 GnmFuncDescriptor const erlang_functions[] = {
286 { "probblock", "ff", help_probblock,
287 gnumeric_probblock, NULL,
288 GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_PERCENT,
289 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC,
290 GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
291 { "offtraf", "ff", help_offtraf,
292 gnumeric_offtraf, NULL,
293 GNM_FUNC_SIMPLE,
294 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC,
295 GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
296 { "dimcirc", "ff", help_dimcirc,
297 gnumeric_dimcirc, NULL,
298 GNM_FUNC_SIMPLE,
299 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC,
300 GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
301 { "offcap", "ff", help_offcap,
302 gnumeric_offcap, NULL,
303 GNM_FUNC_SIMPLE,
304 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC,
305 GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
306 {NULL}
307 };
308