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