1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * fn-erlang.c: Teletraffic functions.
6 * Arief Mulya Utama <arief_m_utama@telkomsel.co.id>
7 * <arief.utama@gmail.com>
10 * Morten Welinder <terra@gnome.org>
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>
29 #include <parse-util.h>
33 #include <gnm-format.h>
36 #include <tools/goal-seek.h>
39 #include <goffice/goffice.h>
40 #include <gnm-plugin.h>
50 guess_carried_traffic (gnm_float traffic
, gnm_float comp_gos
)
52 return traffic
* comp_gos
;
56 calculate_loggos (gnm_float traffic
, gnm_float circuits
)
58 if (traffic
< 0 || circuits
< 1)
61 return (dgamma (traffic
, circuits
+ 1, 1, TRUE
) -
62 pgamma (traffic
, circuits
+ 1, 1, FALSE
, TRUE
));
66 calculate_gos (gnm_float traffic
, gnm_float circuits
, gboolean comp
)
70 /* extra guards wont hurt, right? */
71 if (circuits
< 1 || traffic
< 0)
76 else if (circuits
< 100) {
77 gnm_float cir_iter
= 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
;
86 if (term
< GNM_EPSILON
* sum
)
91 gos
= comp
? sum
/ (1 + sum
) : 1 / (1 + sum
);
93 gnm_float loggos
= calculate_loggos (traffic
, circuits
);
94 gos
= comp
? -gnm_expm1 (loggos
) : gnm_exp (loggos
);
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
}
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
);
123 return value_new_float (gos
);
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
}
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
);
149 return GOAL_SEEK_ERROR
;
150 *y
= guess_carried_traffic (off_traffic
, comp_gos
) - pudata
->traffic
;
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]);
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
);
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
);
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
}
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]);
205 if (des_gos
> 1 || des_gos
<= 0)
206 return value_new_error_VALUE (ei
->pos
);
209 while (calculate_gos (traffic
, high
, FALSE
) > des_gos
) {
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
);
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
}
237 gnm_float circuits
, des_gos
;
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
);
246 return GOAL_SEEK_ERROR
;
247 *y
= gos
- pudata
->des_gos
;
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]);
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
);
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
);
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
,
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
,
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
,
305 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
,
306 GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},