/[dtapublic]/projs/trunk/shared_source/c_tclxtens_7_5/arblenints.c
ViewVC logotype

Contents of /projs/trunk/shared_source/c_tclxtens_7_5/arblenints.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 71 - (show annotations) (download)
Sat Nov 5 11:07:06 2016 UTC (7 years, 4 months ago) by dashley
File MIME type: text/plain
File size: 128542 byte(s)
Set EOL properties appropriately to facilitate simultaneous Linux and Windows development.
1 //$Header$
2 //-------------------------------------------------------------------------------------------------
3 //This file is part of "David T. Ashley's Shared Source Code", a set of shared components
4 //integrated into many of David T. Ashley's projects.
5 //-------------------------------------------------------------------------------------------------
6 //This source code and any program in which it is compiled/used is provided under the MIT License,
7 //reproduced below.
8 //-------------------------------------------------------------------------------------------------
9 //Permission is hereby granted, free of charge, to any person obtaining a copy of
10 //this software and associated documentation files(the "Software"), to deal in the
11 //Software without restriction, including without limitation the rights to use,
12 //copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the
13 //Software, and to permit persons to whom the Software is furnished to do so,
14 //subject to the following conditions :
15 //
16 //The above copyright notice and this permission notice shall be included in all
17 //copies or substantial portions of the Software.
18 //
19 //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
22 //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 //SOFTWARE.
26 //-------------------------------------------------------------------------------------------------
27 #define MODULE_ARBLENINTS
28
29 #include <assert.h>
30 #include <string.h>
31
32 #include "tcl.h"
33 #include "tcldecls.h"
34
35 #include "arblenints.h"
36 #include "bstrfunc.h"
37 #include "extninit.h"
38 #include "gmp_ints.h"
39 #include "gmp_rats.h"
40 #include "gmp_ralg.h"
41 #include "intfunc.h"
42 #include "tclalloc.h"
43
44
45 //Handles the "cfbrapab" subextension.
46 //08/16/01: Visual inspection OK.
47 static
48 int ARBLENINTS_cfbrapab_handler(ClientData dummy,
49 Tcl_Interp *interp,
50 int objc,
51 Tcl_Obj *objv[])
52 {
53 Tcl_Obj *rv;
54
55 //We must have at least two additional arguments
56 //to this extension.
57 if (objc < 4)
58 {
59 Tcl_WrongNumArgs(interp,
60 2,
61 objv,
62 "srn uint_kmax ?uint_hmax? ?options?");
63 return(TCL_ERROR);
64 }
65 else
66 {
67 char *input_arg;
68 int failure, first_dashed_parameter;
69 char *string_result;
70 int string_result_n_allocd;
71 int chars_reqd;
72 int i;
73 int pred_option_specified = 0;
74 int succ_option_specified = 0;
75 int neversmaller_option_specified = 0;
76 int neverlarger_option_specified = 0;
77 int n_option_specified = 0;
78 unsigned n = 0;
79
80 GMP_RATS_mpq_struct q_rn;
81 GMP_INTS_mpz_struct z_kmax;
82 GMP_INTS_mpz_struct z_hmax;
83
84 //Allocate dynamic memory.
85 GMP_RATS_mpq_init(&q_rn);
86 GMP_INTS_mpz_init(&z_kmax);
87 GMP_INTS_mpz_init(&z_hmax);
88
89 //Grab a pointer to the string representation of
90 //the first input argument. The storage does not belong to us.
91 input_arg = Tcl_GetString(objv[2]);
92 assert(input_arg != NULL);
93
94 //Try to parse our first input string as a rational number.
95 //If we are not successful in this, must abort.
96 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
97 &failure,
98 &q_rn);
99
100 if (failure)
101 {
102 rv = Tcl_NewStringObj("arbint cfbrapab: \"", -1);
103 Tcl_AppendToObj(rv, input_arg, -1);
104
105 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
106 Tcl_SetObjResult(interp, rv);
107
108 GMP_RATS_mpq_clear(&q_rn);
109 GMP_INTS_mpz_clear(&z_kmax);
110 GMP_INTS_mpz_clear(&z_hmax);
111
112 return(TCL_ERROR);
113 }
114
115 //Try to parse our next argument as an integer, which
116 //will be KMAX. This must be specified.
117 //
118 //Get string pointer. Storage does not belong to us.
119 input_arg = Tcl_GetString(objv[3]);
120 assert(input_arg != NULL);
121
122 //Try to convert KMAX to an integer. Fatal if an error,
123 //and fatal if the argument is zero or negative.
124 GMP_INTS_mpz_set_general_int(&z_kmax, &failure, input_arg);
125
126 //If there was a parse failure or if the integer is zero
127 //or negative, must flag error.
128 if (failure || GMP_INTS_mpz_is_neg(&z_kmax) || GMP_INTS_mpz_is_zero(&z_kmax))
129 {
130 rv = Tcl_NewStringObj("arbint cfbrapab: \"", -1);
131 Tcl_AppendToObj(rv, input_arg, -1);
132 Tcl_AppendToObj(rv, "\" is not a recognized positive integer.", -1);
133 Tcl_SetObjResult(interp, rv);
134
135 GMP_RATS_mpq_clear(&q_rn);
136 GMP_INTS_mpz_clear(&z_kmax);
137 GMP_INTS_mpz_clear(&z_hmax);
138
139 return(TCL_ERROR);
140 }
141
142 //We need to look for HMAX as the next parameter, if it exists.
143 //The way we will figure this out is by whether the
144 //parameter begins with a "-" or not.
145 if (objc >= 5)
146 {
147 input_arg = Tcl_GetString(objv[4]);
148 assert(input_arg != NULL);
149
150 if (input_arg[0] == '-')
151 {
152 first_dashed_parameter = 4;
153 }
154 else
155 {
156 first_dashed_parameter = 5;
157 }
158 }
159 else
160 {
161 first_dashed_parameter = 4;
162 }
163
164 //If there is another parameter and it
165 //doesn't begin with a dash, try to parse
166 //it as HMAX. We don't explicitly record whether
167 //HMAX is specified, because zero is a signal
168 //when calculating Farey neighbors that HMAX isn't
169 //to be considered.
170 if ((objc >= 5) && (first_dashed_parameter == 5))
171 {
172 //Get string pointer. Storage does not belong to us.
173 input_arg = Tcl_GetString(objv[4]);
174 assert(input_arg != NULL);
175
176 //Try to convert HMAX to an integer. Fatal if an error,
177 //and fatal if the argument is zero or negative.
178 GMP_INTS_mpz_set_general_int(&z_hmax, &failure, input_arg);
179
180 //If there was a parse failure or if the integer is zero
181 //or negative, must flag error.
182 if (failure || GMP_INTS_mpz_is_neg(&z_hmax) || GMP_INTS_mpz_is_zero(&z_hmax))
183 {
184 rv = Tcl_NewStringObj("arbint cfbrapab: \"", -1);
185 Tcl_AppendToObj(rv, input_arg, -1);
186 Tcl_AppendToObj(rv, "\" is not a recognized positive integer.", -1);
187 Tcl_SetObjResult(interp, rv);
188
189 GMP_RATS_mpq_clear(&q_rn);
190 GMP_INTS_mpz_clear(&z_kmax);
191 GMP_INTS_mpz_clear(&z_hmax);
192
193 return(TCL_ERROR);
194 }
195 }
196
197 //Process all of the dashed command-line arguments.
198 //This involves iterating through all of the
199 //parameters and processing them.
200 for (i=first_dashed_parameter; i<objc; i++)
201 {
202 input_arg = Tcl_GetString(objv[i]);
203 assert(input_arg != NULL);
204
205 if (!strcmp("-pred", input_arg))
206 {
207 pred_option_specified = 1;
208 }
209 else if (!strcmp("-succ", input_arg))
210 {
211 succ_option_specified = 1;
212 }
213 else if (!strcmp("-neversmaller", input_arg))
214 {
215 neversmaller_option_specified = 1;
216 }
217 else if (!strcmp("-neverlarger", input_arg))
218 {
219 neverlarger_option_specified = 1;
220 }
221 else if (!strcmp("-n", input_arg))
222 {
223 n_option_specified = 1;
224
225 //If -n was specified, there must be a following
226 //parameter which supplies the integer. First
227 //check for existence of an additional parameter.
228 if (i >= (objc - 1))
229 {
230 rv = Tcl_NewStringObj("arbint cfbrapab: -n option specified without following integer.", -1);
231 Tcl_SetObjResult(interp, rv);
232
233 GMP_RATS_mpq_clear(&q_rn);
234 GMP_INTS_mpz_clear(&z_kmax);
235 GMP_INTS_mpz_clear(&z_hmax);
236
237 return(TCL_ERROR);
238 }
239
240 //We have at least one additional parameter. Try
241 //to parse out the next parameter as the integer
242 //we need for n.
243 i++;
244
245 input_arg = Tcl_GetString(objv[i]);
246 assert(input_arg != NULL);
247
248 GMP_INTS_mpz_parse_into_uint32(&n, &failure, input_arg);
249
250 //If the parse was unsuccessful, terminate.
251 if (failure)
252 {
253 rv = Tcl_NewStringObj("arbint cfbrapab: -n option followed by invalid integer.", -1);
254 Tcl_SetObjResult(interp, rv);
255
256 GMP_RATS_mpq_clear(&q_rn);
257 GMP_INTS_mpz_clear(&z_kmax);
258 GMP_INTS_mpz_clear(&z_hmax);
259
260 return(TCL_ERROR);
261 }
262
263 //Clip the integer into a 24-bit quantity.
264 n &= 0x00FFFFFF;
265 }
266 else
267 {
268 //Unrecognized option. Crash out.
269 rv = Tcl_NewStringObj("arbint cfbrapab: \"", -1);
270 Tcl_AppendToObj(rv, input_arg, -1);
271 Tcl_AppendToObj(rv, "\" is not a recognized option.", -1);
272 Tcl_SetObjResult(interp, rv);
273
274 GMP_RATS_mpq_clear(&q_rn);
275 GMP_INTS_mpz_clear(&z_kmax);
276 GMP_INTS_mpz_clear(&z_hmax);
277
278 return(TCL_ERROR);
279 }
280 }
281
282 //Look for any mutually exclusive options. Give a catchall if any of
283 //them specified. Because we set them all to 1, addition is the easiest
284 //way to do this.
285 if ((pred_option_specified + succ_option_specified + neversmaller_option_specified
286 + neverlarger_option_specified + n_option_specified) > 1)
287 {
288 rv = Tcl_NewStringObj("arbint cfbrapab: -pred, -succ, -neversmaller, -neverlarger, and -n are mutually exclusive options.", -1);
289 Tcl_SetObjResult(interp, rv);
290
291 GMP_RATS_mpq_clear(&q_rn);
292 GMP_INTS_mpz_clear(&z_kmax);
293 GMP_INTS_mpz_clear(&z_hmax);
294
295 return(TCL_ERROR);
296 }
297
298 //Split into cases based on what we're doing. This is wasteful of code,
299 //but this is a PC application, not an embedded application. In all cases
300 //create a hard error if something goes wrong. Any anomalies should trash
301 //a script.
302 if (!pred_option_specified && !succ_option_specified && !n_option_specified)
303 {
304 //This is the traditional best approximation case, with the possibility of
305 //the -neverlarger or -neversmaller being specified. This is the most messy
306 //of all the cases. We must gather neighbors and figure out which is closer,
307 //and if there is a tie, which has the smaller magnitude. It is fairly
308 //messy.
309 GMP_RALG_fab_neighbor_collection_struct neighbor_data;
310 GMP_RATS_mpq_struct left_neigh, right_neigh, diff_left, diff_right, closer_neighbor;
311 int dist_cmp;
312 int mag_cmp;
313
314 //Allocate inner dynamic variables.
315 GMP_RATS_mpq_init(&left_neigh);
316 GMP_RATS_mpq_init(&right_neigh);
317 GMP_RATS_mpq_init(&diff_left);
318 GMP_RATS_mpq_init(&diff_right);
319 GMP_RATS_mpq_init(&closer_neighbor);
320
321 //Form up the neighbor data. We're only looking for up to one neighbor on each
322 //side.
323 GMP_RALG_consecutive_fab_terms(
324 &q_rn,
325 &z_kmax,
326 &z_hmax,
327 1,
328 1,
329 &neighbor_data
330 );
331
332 //If there was an error or we couldn't get any neighbors to play with, give
333 //an error return. As long as we have one neighbor on either side, we can definitely
334 //complete.
335 if (neighbor_data.error || (!neighbor_data.equality && (!neighbor_data.n_left_out || !neighbor_data.n_right_out)))
336 {
337 rv = Tcl_NewStringObj("arbint cfbrapab: unable to form neighbors.", -1);
338 Tcl_SetObjResult(interp, rv);
339
340 GMP_RALG_consecutive_fab_terms_result_free(&neighbor_data);
341 GMP_RATS_mpq_clear(&q_rn);
342 GMP_INTS_mpz_clear(&z_kmax);
343 GMP_INTS_mpz_clear(&z_hmax);
344
345 GMP_RATS_mpq_clear(&left_neigh);
346 GMP_RATS_mpq_clear(&right_neigh);
347 GMP_RATS_mpq_clear(&diff_left);
348 GMP_RATS_mpq_clear(&diff_right);
349 GMP_RATS_mpq_clear(&closer_neighbor);
350
351 return(TCL_ERROR);
352 }
353
354 if (neighbor_data.equality)
355 {
356 //The equality case takes precedence, always.
357 GMP_RATS_mpq_copy(&closer_neighbor, &(neighbor_data.norm_rn));
358 }
359 else
360 {
361 //The boolean test somewhat above guaranteed that we have both left
362 //and right neighbors. We can assume this.
363 GMP_RATS_mpq_copy(&left_neigh, &(neighbor_data.lefts[0].neighbor));
364 GMP_RATS_mpq_copy(&right_neigh, &(neighbor_data.rights[0].neighbor));
365
366 GMP_RATS_mpq_sub(&diff_left, &left_neigh, &(neighbor_data.norm_rn));
367 GMP_RATS_mpq_sub(&diff_right, &right_neigh, &(neighbor_data.norm_rn));
368 GMP_INTS_mpz_abs(&(diff_left.num));
369 GMP_INTS_mpz_abs(&(diff_right.num));
370 dist_cmp = GMP_RATS_mpq_cmp(&diff_left, &diff_right, NULL);
371
372 //If we have a tie on the distance, will need to revert to magnitude of the neighbors.
373 GMP_INTS_mpz_abs(&(left_neigh.num));
374 GMP_INTS_mpz_abs(&(right_neigh.num));
375 mag_cmp = GMP_RATS_mpq_cmp(&left_neigh, &right_neigh, NULL);
376
377 if (!neversmaller_option_specified
378 &&
379 (neverlarger_option_specified || (dist_cmp < 0) || ((dist_cmp==0) && (mag_cmp < 0))))
380 {
381 GMP_RATS_mpq_copy(&closer_neighbor, &(neighbor_data.lefts[0].neighbor));
382 }
383 else
384 {
385 GMP_RATS_mpq_copy(&closer_neighbor, &(neighbor_data.rights[0].neighbor));
386 }
387 }
388
389 //Stuff our variable of choice into a string ...
390 chars_reqd = INTFUNC_max(
391 GMP_INTS_mpz_size_in_base_10(&(closer_neighbor.num)),
392 GMP_INTS_mpz_size_in_base_10(&(closer_neighbor.den))
393 );
394 string_result = TclpAlloc(sizeof(char) * chars_reqd);
395 assert(string_result != NULL);
396
397 GMP_INTS_mpz_to_string(string_result, &(closer_neighbor.num));
398 rv = Tcl_NewStringObj(string_result, -1);
399 Tcl_AppendToObj(rv, "/", -1);
400 GMP_INTS_mpz_to_string(string_result, &(closer_neighbor.den));
401 Tcl_AppendToObj(rv, string_result, -1);
402
403 Tcl_SetObjResult(interp, rv);
404
405 //Deallocate variables, make normal return.
406 TclpFree(string_result);
407 GMP_RALG_consecutive_fab_terms_result_free(&neighbor_data);
408 GMP_RATS_mpq_clear(&q_rn);
409 GMP_INTS_mpz_clear(&z_kmax);
410 GMP_INTS_mpz_clear(&z_hmax);
411
412 GMP_RATS_mpq_clear(&left_neigh);
413 GMP_RATS_mpq_clear(&right_neigh);
414 GMP_RATS_mpq_clear(&diff_left);
415 GMP_RATS_mpq_clear(&diff_right);
416 GMP_RATS_mpq_clear(&closer_neighbor);
417
418 return(TCL_OK);
419 }
420 else if (n_option_specified)
421 {
422 char sbuf[50];
423 //Static buffer just to stage 32-bit integers.
424
425 //Multiple neighbors. Must iterate through.
426
427 GMP_RALG_fab_neighbor_collection_struct neighbor_data;
428
429 //Form up the neighbor data.
430 GMP_RALG_consecutive_fab_terms(
431 &q_rn,
432 &z_kmax,
433 &z_hmax,
434 n,
435 n,
436 &neighbor_data
437 );
438
439 //If there was an error forming up the neighbor data, create a hard error.
440 if (neighbor_data.error)
441 {
442 rv = Tcl_NewStringObj("arbint cfbrapab: unable to form neighbors.", -1);
443 Tcl_SetObjResult(interp, rv);
444
445 GMP_RALG_consecutive_fab_terms_result_free(&neighbor_data);
446 GMP_RATS_mpq_clear(&q_rn);
447 GMP_INTS_mpz_clear(&z_kmax);
448 GMP_INTS_mpz_clear(&z_hmax);
449
450 return(TCL_ERROR);
451 }
452
453 //Allocate a default buffer of 10K for the ASCII representation of integers.
454 //In the vast majority of cases, there will be only one allocation, because it
455 //takes a mean integer to exceed 10K. However, the logic allows it to grow.
456 string_result_n_allocd = 10000;
457 string_result = TclpAlloc(sizeof(char) * string_result_n_allocd);
458 assert(string_result != NULL);
459
460 //Start off with a return value of the null string.
461 rv = Tcl_NewStringObj("", -1);
462
463 //Loop through, spitting out the left neighbors.
464 for (i = neighbor_data.n_left_out-1; i >= 0; i--)
465 {
466 //The protocol here is everyone spits out one space before
467 //they print anything. Must skip this on first loop iteration.
468 if (i != neighbor_data.n_left_out-1)
469 Tcl_AppendToObj(rv, " ", -1);
470
471 //The index will be the negative of the iteration variable minus one.
472 sprintf(sbuf, "%d", -i - 1);
473 Tcl_AppendToObj(rv, sbuf, -1);
474
475 //Force the buffer to have enough space for the components of the rational
476 //number.
477 chars_reqd = INTFUNC_max(
478 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.lefts[i].neighbor.num)),
479 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.lefts[i].neighbor.den))
480 );
481 if (chars_reqd > string_result_n_allocd)
482 {
483 string_result_n_allocd = chars_reqd;
484 string_result = TclpRealloc(string_result, sizeof(char) * string_result_n_allocd);
485 assert(string_result != NULL);
486 }
487
488 //Print the rational number out to the Tcl object.
489 Tcl_AppendToObj(rv, " ", -1);
490 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.lefts[i].neighbor.num));
491 Tcl_AppendToObj(rv, string_result, -1);
492 Tcl_AppendToObj(rv, "/", -1);
493 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.lefts[i].neighbor.den));
494 Tcl_AppendToObj(rv, string_result, -1);
495 }
496
497 //Spit out the equality case if appropriate.
498 if (neighbor_data.equality)
499 {
500 if (neighbor_data.n_left_out)
501 Tcl_AppendToObj(rv, " ", -1);
502
503 Tcl_AppendToObj(rv, "0", -1);
504
505 //Force the buffer to have enough space for the components of the rational
506 //number.
507 chars_reqd = INTFUNC_max(
508 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.norm_rn.num)),
509 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.norm_rn.den))
510 );
511 if (chars_reqd > string_result_n_allocd)
512 {
513 string_result_n_allocd = chars_reqd;
514 string_result = TclpRealloc(string_result, sizeof(char) * string_result_n_allocd);
515 assert(string_result != NULL);
516 }
517
518 //Print the rational number out to the Tcl object.
519 Tcl_AppendToObj(rv, " ", -1);
520 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.norm_rn.num));
521 Tcl_AppendToObj(rv, string_result, -1);
522 Tcl_AppendToObj(rv, "/", -1);
523 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.norm_rn.den));
524 Tcl_AppendToObj(rv, string_result, -1);
525 }
526
527 //Loop through, spitting out the right neighbors.
528 for (i = 0; i < neighbor_data.n_right_out; i++)
529 {
530 //The protocol here is everyone spits out one space before
531 //they print anything. Must skip this on first loop iteration.
532 if (neighbor_data.n_left_out || neighbor_data.equality || i)
533 Tcl_AppendToObj(rv, " ", -1);
534
535 //The index will be the iteration variable plus one.
536 sprintf(sbuf, "%d", i+1);
537 Tcl_AppendToObj(rv, sbuf, -1);
538
539 //Force the buffer to have enough space for the components of the rational
540 //number.
541 chars_reqd = INTFUNC_max(
542 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.rights[i].neighbor.num)),
543 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.rights[i].neighbor.den))
544 );
545 if (chars_reqd > string_result_n_allocd)
546 {
547 string_result_n_allocd = chars_reqd;
548 string_result = TclpRealloc(string_result, sizeof(char) * string_result_n_allocd);
549 assert(string_result != NULL);
550 }
551
552 //Print the rational number out to the Tcl object.
553 Tcl_AppendToObj(rv, " ", -1);
554 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.rights[i].neighbor.num));
555 Tcl_AppendToObj(rv, string_result, -1);
556 Tcl_AppendToObj(rv, "/", -1);
557 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.rights[i].neighbor.den));
558 Tcl_AppendToObj(rv, string_result, -1);
559 }
560
561 //Set up for a normal return.
562 Tcl_SetObjResult(interp, rv);
563
564 TclpFree(string_result);
565 GMP_RALG_consecutive_fab_terms_result_free(&neighbor_data);
566 GMP_RATS_mpq_clear(&q_rn);
567 GMP_INTS_mpz_clear(&z_kmax);
568 GMP_INTS_mpz_clear(&z_hmax);
569
570 return(TCL_OK);
571 }
572 else if (pred_option_specified)
573 {
574 //Simple predecessor case.
575
576 GMP_RALG_fab_neighbor_collection_struct neighbor_data;
577
578 //Form up the neighbor data.
579 GMP_RALG_consecutive_fab_terms(
580 &q_rn,
581 &z_kmax,
582 &z_hmax,
583 1,
584 0,
585 &neighbor_data
586 );
587
588 //If there was an error forming up the neighbor data or there are no left neighbors,
589 //create a hard error.
590 if (neighbor_data.error || !neighbor_data.n_left_out)
591 {
592 rv = Tcl_NewStringObj("arbint cfbrapab: unable to find predecessor.", -1);
593 Tcl_SetObjResult(interp, rv);
594
595 GMP_RALG_consecutive_fab_terms_result_free(&neighbor_data);
596 GMP_RATS_mpq_clear(&q_rn);
597 GMP_INTS_mpz_clear(&z_kmax);
598 GMP_INTS_mpz_clear(&z_hmax);
599
600 return(TCL_ERROR);
601 }
602
603 //The test above confirmed that we have at least one left neighbor calculated.
604 //We can dump it to a string and finish up.
605 chars_reqd = INTFUNC_max(
606 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.lefts[0].neighbor.num)),
607 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.lefts[0].neighbor.den))
608 );
609 string_result = TclpAlloc(sizeof(char) * chars_reqd);
610 assert(string_result != NULL);
611
612 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.lefts[0].neighbor.num));
613 rv = Tcl_NewStringObj(string_result, -1);
614 Tcl_AppendToObj(rv, "/", -1);
615 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.lefts[0].neighbor.den));
616 Tcl_AppendToObj(rv, string_result, -1);
617
618 Tcl_SetObjResult(interp, rv);
619
620 TclpFree(string_result);
621 GMP_RALG_consecutive_fab_terms_result_free(&neighbor_data);
622 GMP_RATS_mpq_clear(&q_rn);
623 GMP_INTS_mpz_clear(&z_kmax);
624 GMP_INTS_mpz_clear(&z_hmax);
625
626 return(TCL_OK);
627 }
628 else if (succ_option_specified)
629 {
630 //Simple successor.
631
632 GMP_RALG_fab_neighbor_collection_struct neighbor_data;
633
634 //Form up the neighbor data.
635 GMP_RALG_consecutive_fab_terms(
636 &q_rn,
637 &z_kmax,
638 &z_hmax,
639 0,
640 1,
641 &neighbor_data
642 );
643
644 //If there was an error forming up the neighbor data or there are no right neighbors,
645 //create a hard error.
646 if (neighbor_data.error || !neighbor_data.n_right_out)
647 {
648 rv = Tcl_NewStringObj("arbint cfbrapab: unable to find successor.", -1);
649 Tcl_SetObjResult(interp, rv);
650
651 GMP_RALG_consecutive_fab_terms_result_free(&neighbor_data);
652 GMP_RATS_mpq_clear(&q_rn);
653 GMP_INTS_mpz_clear(&z_kmax);
654 GMP_INTS_mpz_clear(&z_hmax);
655
656 return(TCL_ERROR);
657 }
658
659 //The test above confirmed that we have at least one right neighbor calculated.
660 //We can dump it to a string and finish up.
661 chars_reqd = INTFUNC_max(
662 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.rights[0].neighbor.num)),
663 GMP_INTS_mpz_size_in_base_10(&(neighbor_data.rights[0].neighbor.den))
664 );
665 string_result = TclpAlloc(sizeof(char) * chars_reqd);
666 assert(string_result != NULL);
667
668 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.rights[0].neighbor.num));
669 rv = Tcl_NewStringObj(string_result, -1);
670 Tcl_AppendToObj(rv, "/", -1);
671 GMP_INTS_mpz_to_string(string_result, &(neighbor_data.rights[0].neighbor.den));
672 Tcl_AppendToObj(rv, string_result, -1);
673
674 Tcl_SetObjResult(interp, rv);
675
676 TclpFree(string_result);
677 GMP_RALG_consecutive_fab_terms_result_free(&neighbor_data);
678 GMP_RATS_mpq_clear(&q_rn);
679 GMP_INTS_mpz_clear(&z_kmax);
680 GMP_INTS_mpz_clear(&z_hmax);
681
682 return(TCL_OK);
683 }
684
685 //Free up all dynamic memory.
686 GMP_RATS_mpq_clear(&q_rn);
687 GMP_INTS_mpz_clear(&z_kmax);
688 GMP_INTS_mpz_clear(&z_hmax);
689
690 //Return
691 return(TCL_OK);
692 }
693 }
694
695
696 //Handles the "cfratnum" subextension.
697 //08/07/01: Visually inspected, OK.
698 static
699 int ARBLENINTS_cfratnum_handler(ClientData dummy,
700 Tcl_Interp *interp,
701 int objc,
702 Tcl_Obj *objv[])
703 {
704 Tcl_Obj *rv;
705
706 //We must have exactly one additional argument
707 //to this function, which is the rational number
708 //whose continued fraction decomposition is to be
709 //calculated.
710 if (objc != 3)
711 {
712 Tcl_WrongNumArgs(interp,
713 2,
714 objv,
715 "urn");
716 return(TCL_ERROR);
717 }
718 else
719 {
720 char *input_arg;
721 int failure;
722 unsigned chars_reqd;
723 char *string_result;
724 int n_string_result;
725 int i;
726 GMP_RATS_mpq_struct rn;
727 GMP_RALG_cf_app_struct decomp;
728
729 //In this function, we are going to return a string
730 //result formed by starting with a string and then
731 //concatenating to it again and again. We start
732 //off believing that 10,000 characters of space is enough,
733 //but we may need to revise upward and reallocate.
734 //A 10,000 character block is chosen because it is quick
735 //to allocate and most times won't go beyond that.
736 n_string_result = 10000;
737 string_result = TclpAlloc(sizeof(char) * n_string_result);
738 assert(string_result != NULL);
739
740 //We will need a rational number to hold the return value
741 //from the parsing function. Allocate that now.
742 GMP_RATS_mpq_init(&rn);
743
744 //Grab a pointer to the string representation of
745 //the input argument. The storage does not belong to us.
746 input_arg = Tcl_GetString(objv[2]);
747 assert(input_arg != NULL);
748
749 //Try to parse our input string as a rational number.
750 //If we are not successful in this, must abort.
751 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
752 &failure,
753 &rn);
754
755 if (failure)
756 {
757 rv = Tcl_NewStringObj("arbint cfratnum: \"", -1);
758 Tcl_AppendToObj(rv, input_arg, -1);
759
760 Tcl_AppendToObj(rv, "\" is not a recognized non-negative rational number.", -1);
761 Tcl_SetObjResult(interp, rv);
762
763 TclpFree(string_result);
764 GMP_RATS_mpq_clear(&rn);
765
766 return(TCL_ERROR);
767 }
768
769 //OK, we have a rational number, but there is a possibility
770 //it is negative, which is a no-no. Normalize the signs
771 //for easier testing.
772 GMP_RATS_mpq_normalize_sign(&rn);
773 if (GMP_INTS_mpz_is_neg(&(rn.num)))
774 {
775 rv = Tcl_NewStringObj("arbint cfratnum: \"", -1);
776 Tcl_AppendToObj(rv, input_arg, -1);
777
778 Tcl_AppendToObj(rv, "\" is negative.", -1);
779 Tcl_SetObjResult(interp, rv);
780
781 TclpFree(string_result);
782 GMP_RATS_mpq_clear(&rn);
783
784 return(TCL_ERROR);
785 }
786
787 //OK, we have a rational number. Form the continued fraction
788 //decomposition of it. The function called is set up so that
789 //one must deallocate, even in an error condition.
790 GMP_RALG_cfdecomp_init(&decomp,
791 &failure,
792 &(rn.num),
793 &(rn.den));
794
795 //If we failed in the decomposition (don't know why that would
796 //happen) use the general error flag "NAN".
797 if (failure)
798 {
799 rv = Tcl_NewStringObj("NAN", -1);
800
801 Tcl_SetObjResult(interp, rv);
802
803 TclpFree(string_result);
804 GMP_RATS_mpq_clear(&rn);
805 GMP_RALG_cfdecomp_destroy(&decomp);
806
807 return(TCL_ERROR);
808 }
809
810 //OK, that really is the last error we could have.
811 //Iterate, adding the partial quotients and convergents
812 //to the string which we'll return. We need to watch out
813 //for running over our 10K buffer.
814 rv = Tcl_NewStringObj("", -1);
815 for (i=0; i<decomp.n; i++)
816 {
817 //Partial quotient.
818 chars_reqd = GMP_INTS_mpz_size_in_base_10(&(decomp.a[i]));
819 if (chars_reqd > (unsigned)n_string_result)
820 {
821 n_string_result = chars_reqd;
822 string_result = TclpRealloc(string_result,
823 sizeof(char) * n_string_result);
824 }
825 GMP_INTS_mpz_to_string(string_result, &(decomp.a[i]));
826 Tcl_AppendToObj(rv, string_result, -1);
827 Tcl_AppendToObj(rv, " ", -1);
828
829 //Numerator of convergent.
830 chars_reqd = GMP_INTS_mpz_size_in_base_10(&(decomp.p[i]));
831 if (chars_reqd > (unsigned)n_string_result)
832 {
833 n_string_result = chars_reqd;
834 string_result = TclpRealloc(string_result,
835 sizeof(char) * n_string_result);
836 }
837 GMP_INTS_mpz_to_string(string_result, &(decomp.p[i]));
838 Tcl_AppendToObj(rv, string_result, -1);
839 Tcl_AppendToObj(rv, " ", -1);
840
841 //Denominator of convergent.
842 chars_reqd = GMP_INTS_mpz_size_in_base_10(&(decomp.q[i]));
843 if (chars_reqd > (unsigned)n_string_result)
844 {
845 n_string_result = chars_reqd;
846 string_result = TclpRealloc(string_result,
847 sizeof(char) * n_string_result);
848 }
849 GMP_INTS_mpz_to_string(string_result, &(decomp.q[i]));
850 Tcl_AppendToObj(rv, string_result, -1);
851 if (i != (decomp.n - 1)) //No space after last number.
852 Tcl_AppendToObj(rv, " ", -1);
853 }
854
855 //Assign the result to be the return value.
856 Tcl_SetObjResult(interp, rv);
857
858 //Free up all dynamic memory.
859 TclpFree(string_result);
860 GMP_RATS_mpq_clear(&rn);
861 GMP_RALG_cfdecomp_destroy(&decomp);
862
863 //Return
864 return(TCL_OK);
865 }
866 }
867
868
869 //Handles the "commanate" subextension.
870 //07/29/01: Visual inspection OK. Have not located my Tcl book, am doing this
871 //from memory an intuition as far as how to set return results and so forth.
872 static
873 int ARBLENINTS_commanate_handler(ClientData dummy,
874 Tcl_Interp *interp,
875 int objc,
876 Tcl_Obj *objv[])
877 {
878 Tcl_Obj *rv;
879
880 //We must have one and exactly one additional argument
881 //to this function, which is the string we want to
882 //commanate.
883 if (objc != 3)
884 {
885 Tcl_WrongNumArgs(interp,
886 2,
887 objv,
888 "sint");
889 return(TCL_ERROR);
890 }
891 else
892 {
893 char *string_arg;
894
895 //Grab a pointer to the string representation of
896 //the input argument. The storage does not belong to us.
897 string_arg = Tcl_GetString(objv[2]);
898 assert(string_arg != NULL);
899
900 //Try to parse the string as one of the error tags.
901 //If it is one of those, it isn't an error, but don't
902 //want to touch the string.
903 if (GMP_INTS_identify_nan_string(string_arg) >= 0)
904 {
905 rv = Tcl_NewStringObj(string_arg, -1);
906 Tcl_SetObjResult(interp, rv);
907 return(TCL_OK);
908 }
909 //Try to parse it as a signed integer with commas already.
910 //If it already has commas, there is no need to add any.
911 else if (BSTRFUNC_is_sint_w_commas(string_arg))
912 {
913 //This is already an acceptable commanated signed integer. Send it
914 //back as the return value.
915 rv = Tcl_NewStringObj(string_arg, -1);
916 Tcl_SetObjResult(interp, rv);
917 return(TCL_OK);
918 }
919 //Try to parse the argument as a signed integer without commas.
920 //If it is one of those, commanate it and return it.
921 else if (BSTRFUNC_is_sint_wo_commas(string_arg))
922 {
923 size_t len;
924 char *buffer;
925
926 len = strlen(string_arg);
927 buffer = TclpAlloc(((sizeof(char) * 4 * len) / 3) + 10);
928 strcpy(buffer, string_arg);
929 BSTRFUNC_commanate(buffer);
930 rv = Tcl_NewStringObj(buffer, -1);
931 TclpFree(buffer);
932 Tcl_SetObjResult(interp, rv);
933 return(TCL_OK);
934 }
935 else
936 {
937 //Error case. Must give error message.
938 rv = Tcl_NewStringObj("arbint commanate: \"", -1);
939 Tcl_AppendToObj(rv, string_arg, -1);
940 Tcl_AppendToObj(rv, "\" is not a recognized integer.", -1);
941 Tcl_SetObjResult(interp, rv);
942 return(TCL_ERROR);
943 }
944 }
945 }
946
947
948 //Handles the "const" subextension.
949 //08/17/01: Visual inspection OK.
950 static
951 int ARBLENINTS_const_handler(ClientData dummy,
952 Tcl_Interp *interp,
953 int objc,
954 Tcl_Obj *objv[])
955 {
956 //Table of constants used.
957 static struct
958 {
959 char *tag;
960 //The symbolic tag used to identify the number.
961 char *desc;
962 //The full description of the number. It must consist
963 //of a string with lines no longer than about 70 chars,
964 //separated by newlines, and indented by 6 spaces.
965 char *minmant;
966 //The minimum mantissa or minimum representation.
967 //May not be empty or NULL.
968 char *mantrem;
969 //The remaining mantissa or remaining portion of
970 //number. May be empty, but may not be NULL.
971 char *exp;
972 //The exponent portion, if any, or NULL otherwise.
973 int deflen;
974 //The default number of digits for the constant
975 //if none is specified.
976 int digit_count_offset;
977 //The offset to go from string length of mantissa
978 //portions to number of digits. Cheap way to adjust
979 //for - sign and decimal point.
980 } tbl[] =
981 {
982 //e--the transcendental number e.
983 {
984 //tag
985 "e",
986 //desc
987 " Historically significant transcendental constant. Digits obtained\n"
988 " from http://fermi.udw.ac.za/physics/e.html on 08/17/01.",
989 //minmant
990 "2.7",
991 //mantrem
992 "182818284590452353602874713526624977572470936999595749669676277240766303535"
993 "475945713821785251664274274663919320030599218174135966290435729003342952605956"
994 "307381323286279434907632338298807531952510190115738341879307021540891499348841"
995 "675092447614606680822648001684774118537423454424371075390777449920695517027618"
996 "386062613313845830007520449338265602976067371132007093287091274437470472306969"
997 "772093101416928368190255151086574637721112523897844250569536967707854499699679"
998 "468644549059879316368892300987931277361782154249992295763514822082698951936680"
999 "331825288693984964651058209392398294887933203625094431173012381970684161403970"
1000 "198376793206832823764648042953118023287825098194558153017567173613320698112509"
1001 "961818815930416903515988885193458072738667385894228792284998920868058257492796"
1002 "104841984443634632449684875602336248270419786232090021609902353043699418491463"
1003 "140934317381436405462531520961836908887070167683964243781405927145635490613031"
1004 "07208510383750510115747704171898610687396965521267154688957035035",
1005 //exp
1006 NULL,
1007 //deflen
1008 30,
1009 //digit_count_offset
1010 1
1011 },
1012 //g_metric
1013 {
1014 //tag
1015 "g_si",
1016 //desc
1017 " Gravitational acceleration in SI units, meters per second**2.\n"
1018 " Obtained from NIST Special Publication 811 on 08/17/01.",
1019 //minmant
1020 "9.80665",
1021 //mantrem
1022 "",
1023 //exp
1024 NULL,
1025 //deflen
1026 30,
1027 //digit_count_offset
1028 1
1029 },
1030 //in2m
1031 {
1032 //tag
1033 "in2m",
1034 //desc
1035 " Multiplicative conversion factor from inches to meters.\n"
1036 " Obtained from NIST Special Publication 811 on 08/17/01.",
1037 //minmant
1038 "2.54",
1039 //mantrem
1040 "",
1041 //exp
1042 "e-2",
1043 //deflen
1044 30,
1045 //digit_count_offset
1046 1
1047 },
1048 //mi2km
1049 {
1050 //tag
1051 "mi2km",
1052 //desc
1053 " Multiplicative conversion factor from miles to kilometers.\n"
1054 " Obtained from NIST Special Publication 811 on 08/17/01.",
1055 //minmant
1056 "1.609344",
1057 //mantrem
1058 "",
1059 //exp
1060 NULL,
1061 //deflen
1062 30,
1063 //digit_count_offset
1064 1
1065 },
1066 //pi--the transcendental number PI.
1067 {
1068 //tag
1069 "pi",
1070 //desc
1071 " Transcendental constant supplying ratio of a circle's circumference\n"
1072 " to its diameter. Digits obtained from http://www.joyofpi.com/\n"
1073 " pi.htm on 08/17/01.",
1074 //minmant
1075 "3.14",
1076 //mantrem
1077 "15926535897932384626433832795028841971"
1078 "6939937510582097494459230781640628620899"
1079 "8628034825342117067982148086513282306647"
1080 "0938446095505822317253594081284811174502"
1081 "8410270193852110555964462294895493038196"
1082 "4428810975665933446128475648233786783165"
1083 "2712019091456485669234603486104543266482"
1084 "1339360726024914127372458700660631558817"
1085 "4881520920962829254091715364367892590360"
1086 "0113305305488204665213841469519415116094"
1087 "3305727036575959195309218611738193261179"
1088 "3105118548074462379962749567351885752724"
1089 "8912279381830119491298336733624406566430"
1090 "8602139494639522473719070217986094370277"
1091 "0539217176293176752384674818467669405132"
1092 "0005681271452635608277857713427577896091"
1093 "7363717872146844090122495343014654958537"
1094 "1050792279689258923542019956112129021960"
1095 "8640344181598136297747713099605187072113"
1096 "4999999837297804995105973173281609631859"
1097 "5024459455346908302642522308253344685035"
1098 "2619311881710100031378387528865875332083"
1099 "8142061717766914730359825349042875546873"
1100 "1159562863882353787593751957781857780532"
1101 "1712268066130019278766111959092164201989"
1102 "3809525720106548586327886593615338182796"
1103 "8230301952035301852968995773622599413891"
1104 "2497217752834791315155748572424541506959"
1105 "5082953311686172785588907509838175463746"
1106 "4939319255060400927701671139009848824012",
1107 //exp
1108 NULL,
1109 //deflen
1110 30,
1111 //digit_count_offset
1112 1
1113 },
1114 //sqrt5--the square root of 5.
1115 {
1116 //tag
1117 "sqrt5",
1118 //desc
1119 " The square root of 5. Digits obtained from\n"
1120 " http://home.earthlink.net/~maryski/sqrt51000000.txt on 08/17/01.",
1121 //minmant
1122 "2.236",
1123 //mantrem
1124 "0679774997896964091736687312762354406183596115257242708972454105209256378048"
1125 "99414414408378782274969508176150773783504253267724447073863586360121533452708866"
1126 "77817319187916581127664532263985658053576135041753378500342339241406444208643253"
1127 "90972525926272288762995174024406816117759089094984923713907297288984820886415426"
1128 "89894099131693577019748678884425089754132956183176921499977424801530434115035957"
1129 "66833251249881517813940800056242085524354223555610630634282023409333198293395974"
1130 "63522712013417496142026359047378855043896870611356600457571399565955669569175645"
1131 "78221952500060539231234005009286764875529722056766253666074485853505262330678494"
1132 "63342224231763727702663240768010444331582573350589309813622634319868647194698997"
1133 "01808189524264459620345221411922329125981963258111041704958070481204034559949435"
1134 "06855551855572512388641655010262436312571024449618789424682903404474716115455723"
1135 "20173767659046091852957560357798439805415538077906439363972302875606299948221385"
1136 "21773485924535151210463455550407072278724215347787529112121211843317893351910380",
1137 //exp
1138 NULL,
1139 //deflen
1140 30,
1141 //digit_count_offset
1142 1
1143 },
1144 };
1145
1146 Tcl_Obj *rv;
1147 //Value that will be returned to caller.
1148 int i;
1149 //Iteration variable.
1150 int tbl_idx;
1151 //Index into lookup table, of -1 if not found.
1152 int ndigits;
1153 //The number of digits to supply.
1154 int result_code;
1155 //Return value from Tcl library function.
1156
1157 //We must have either one or two additional arguments.
1158 if ((objc != 3) && (objc != 4))
1159 {
1160 Tcl_WrongNumArgs(interp,
1161 2,
1162 objv,
1163 "constant_tag ?ndigits?");
1164 return(TCL_ERROR);
1165 }
1166 else
1167 {
1168 char *string_arg;
1169
1170 //Grab a pointer to the string representation of
1171 //the input argument. The storage does not belong to us.
1172 string_arg = Tcl_GetString(objv[2]);
1173 assert(string_arg != NULL);
1174
1175 //Try to look up the string argument in the table.
1176 tbl_idx = -1;
1177 for (i=0; i<sizeof(tbl)/sizeof(tbl[0]); i++)
1178 {
1179 if (!strcmp(string_arg, tbl[i].tag))
1180 {
1181 tbl_idx = i;
1182 break;
1183 }
1184 }
1185
1186 //If the tag was not found in the table, print a hostile
1187 //message and abort.
1188 if (tbl_idx == -1)
1189 {
1190 char buf[100];
1191
1192 //Error case. Must give error message.
1193 //Must also list the constants available.
1194 rv = Tcl_NewStringObj("arbint const: \"", -1);
1195 Tcl_AppendToObj(rv, string_arg, -1);
1196 Tcl_AppendToObj(rv, "\" is not a recognized constant.\n", -1);
1197
1198 Tcl_AppendToObj(rv, "Available constants are:\n", -1);
1199
1200 for (i=0; i<sizeof(tbl)/sizeof(tbl[0]); i++)
1201 {
1202 Tcl_AppendToObj(rv, " ", -1);
1203 Tcl_AppendToObj(rv, tbl[i].tag, -1);
1204 sprintf(buf, " (%d digits available)\n",
1205 strlen(tbl[i].minmant) + strlen(tbl[i].mantrem) - tbl[i].digit_count_offset);
1206 Tcl_AppendToObj(rv, buf, -1);
1207 Tcl_AppendToObj(rv, tbl[i].desc, -1);
1208 if (i != (sizeof(tbl)/sizeof(tbl[0]) - 1))
1209 Tcl_AppendToObj(rv, "\n", -1);
1210 }
1211
1212 Tcl_SetObjResult(interp, rv);
1213 return(TCL_ERROR);
1214 }
1215
1216 //Make assertions about the string pointers.
1217 assert(tbl[tbl_idx].tag != NULL);
1218 assert(tbl[tbl_idx].desc != NULL);
1219 assert(tbl[tbl_idx].minmant != NULL);
1220 assert(tbl[tbl_idx].mantrem != NULL);
1221
1222 //Assume the default number of digits by default.
1223 ndigits = tbl[tbl_idx].deflen;
1224
1225 //If there is an additional parameter, try to interpret
1226 //that as the number of digits.
1227 if (objc == 4)
1228 {
1229 //SetIntFromAny(interp, objPtr)
1230 result_code = Tcl_GetIntFromObj(NULL, objv[3], &ndigits);
1231
1232 if (result_code != TCL_OK)
1233 {
1234 //Could not obtain an integer. Use hostile error
1235 //message and abort.
1236 string_arg = Tcl_GetString(objv[3]);
1237 assert(string_arg != NULL);
1238
1239 rv = Tcl_NewStringObj("arbint const: \"", -1);
1240 Tcl_AppendToObj(rv, string_arg, -1);
1241 Tcl_AppendToObj(rv, "\" is not a recognized integer.", -1);
1242 Tcl_SetObjResult(interp, rv);
1243 return(TCL_ERROR);
1244 }
1245 }
1246
1247 //Ndigits may be corrupt. We have to be careful below to not
1248 //allow any funny stuff.
1249 rv = Tcl_NewStringObj(tbl[tbl_idx].minmant, -1);
1250 ndigits = ndigits - strlen(tbl[tbl_idx].minmant) + tbl[tbl_idx].digit_count_offset;
1251 if (ndigits > 0)
1252 {
1253 if (ndigits >= (int)strlen(tbl[tbl_idx].mantrem))
1254 {
1255 Tcl_AppendToObj(rv, tbl[tbl_idx].mantrem, -1);
1256 }
1257 else
1258 {
1259 Tcl_AppendToObj(rv, tbl[tbl_idx].mantrem, ndigits);
1260 }
1261 }
1262
1263 //Append the exponent portion.
1264 if (tbl[tbl_idx].exp)
1265 Tcl_AppendToObj(rv, tbl[tbl_idx].exp, -1);
1266
1267 //Default successful return.
1268 Tcl_SetObjResult(interp, rv);
1269 return(TCL_OK);
1270 }
1271 }
1272
1273
1274 //Handles the "decommanate" subextension.
1275 //07/29/01: Visual inspection OK. Have not located my Tcl book, am doing this
1276 //from memory an intuition as far as how to set return results and so forth.
1277 static
1278 int ARBLENINTS_decommanate_handler(ClientData dummy,
1279 Tcl_Interp *interp,
1280 int objc,
1281 Tcl_Obj *objv[])
1282 {
1283 Tcl_Obj *rv;
1284
1285 //We must have one and exactly one additional argument
1286 //to this function, which is the string we want to
1287 //decommanate.
1288 if (objc != 3)
1289 {
1290 Tcl_WrongNumArgs(interp,
1291 2,
1292 objv,
1293 "sint");
1294 return(TCL_ERROR);
1295 }
1296 else
1297 {
1298 char *string_arg;
1299
1300 //Grab a pointer to the string representation of
1301 //the input argument. The storage does not belong to us.
1302 string_arg = Tcl_GetString(objv[2]);
1303 assert(string_arg != NULL);
1304
1305 //Try to parse the string as one of the error tags.
1306 //If it is one of those, it isn't an error, but don't
1307 //want to touch the string.
1308 if (GMP_INTS_identify_nan_string(string_arg) >= 0)
1309 {
1310 rv = Tcl_NewStringObj(string_arg, -1);
1311 Tcl_SetObjResult(interp, rv);
1312 return(TCL_OK);
1313 }
1314 //Try to parse it as a signed integer without commas.
1315 //If it has no commas, there is no need to decommanate it.
1316 else if (BSTRFUNC_is_sint_wo_commas(string_arg))
1317 {
1318 //This is already an acceptable commanated signed integer. Send it
1319 //back as the return value.
1320 rv = Tcl_NewStringObj(string_arg, -1);
1321 Tcl_SetObjResult(interp, rv);
1322 return(TCL_OK);
1323 }
1324 //Try to parse the argument as a signed integer with commas.
1325 //If it is one of those, decommanate it and return it.
1326 else if (BSTRFUNC_is_sint_w_commas(string_arg))
1327 {
1328 size_t len;
1329 char *buffer;
1330
1331 len = strlen(string_arg);
1332 buffer = TclpAlloc(sizeof(char) * len + 1);
1333 strcpy(buffer, string_arg);
1334 BSTRFUNC_decommanate(buffer);
1335 rv = Tcl_NewStringObj(buffer, -1);
1336 TclpFree(buffer);
1337 Tcl_SetObjResult(interp, rv);
1338 return(TCL_OK);
1339 }
1340 else
1341 {
1342 //Error case. Must give error message.
1343 rv = Tcl_NewStringObj("arbint decommanate: \"", -1);
1344 Tcl_AppendToObj(rv, string_arg, -1);
1345 Tcl_AppendToObj(rv, "\" is not a recognized integer.", -1);
1346 Tcl_SetObjResult(interp, rv);
1347 return(TCL_ERROR);
1348 }
1349 }
1350 }
1351
1352
1353 //Handles the "intadd" subextension.
1354 //08/06/01: Visual inspection OK.
1355 static
1356 int ARBLENINTS_intadd_handler(ClientData dummy,
1357 Tcl_Interp *interp,
1358 int objc,
1359 Tcl_Obj *objv[])
1360 {
1361 Tcl_Obj *rv;
1362
1363 //We must have two and exactly two additional arguments
1364 //to this function, which are the integers whose
1365 //sum is to be calculated.
1366 if (objc != 4)
1367 {
1368 Tcl_WrongNumArgs(interp,
1369 2,
1370 objv,
1371 "sint sint");
1372 return(TCL_ERROR);
1373 }
1374 else
1375 {
1376 GMP_INTS_mpz_struct arb_arg1, arb_arg2, arb_result;
1377 char *add_arg1, *add_arg2;
1378 int failure1, failure2;
1379 unsigned chars_reqd;
1380 char *string_result;
1381 int i, j;
1382
1383 //Allocate space for the arbitrary-length integer result.
1384 GMP_INTS_mpz_init(&arb_arg1);
1385 GMP_INTS_mpz_init(&arb_arg2);
1386 GMP_INTS_mpz_init(&arb_result);
1387
1388 //Grab pointers to the string representation of
1389 //the input arguments. The storage does not belong to us.
1390 add_arg1 = Tcl_GetString(objv[2]);
1391 assert(add_arg1 != NULL);
1392 add_arg2 = Tcl_GetString(objv[3]);
1393 assert(add_arg2 != NULL);
1394
1395 //Try to interpret either of the strings as one of the NAN tags.
1396 //If it is one, return the appropriate result for
1397 //a binary operation.
1398 i = GMP_INTS_identify_nan_string(add_arg1);
1399 j = GMP_INTS_identify_nan_string(add_arg2);
1400
1401 if ((i >= 0) || (j >= 0))
1402 {
1403 const char *p;
1404
1405 //Find the max of i and j. This isn't a scientific way to tag the
1406 //result, but will be OK. Some information is lost no matter what
1407 //we do.
1408 if (i > j)
1409 ;
1410 else
1411 i = j;
1412
1413 //i now contains the max.
1414 switch (i)
1415 {
1416 case 0: p = GMP_INTS_supply_nan_string(2);
1417 break;
1418 case 1: p = GMP_INTS_supply_nan_string(3);
1419 break;
1420 case 2: p = GMP_INTS_supply_nan_string(2);
1421 break;
1422 case 3: p = GMP_INTS_supply_nan_string(3);
1423 break;
1424 default:
1425 assert(0);
1426 break;
1427 }
1428
1429 rv = Tcl_NewStringObj(p, -1);
1430 Tcl_SetObjResult(interp, rv);
1431
1432 GMP_INTS_mpz_clear(&arb_arg1);
1433 GMP_INTS_mpz_clear(&arb_arg2);
1434 GMP_INTS_mpz_clear(&arb_result);
1435
1436 return(TCL_OK);
1437 }
1438
1439 //Try to convert both strings into arbitrary integers.
1440 GMP_INTS_mpz_set_general_int(&arb_arg1, &failure1, add_arg1);
1441 GMP_INTS_mpz_set_general_int(&arb_arg2, &failure2, add_arg2);
1442
1443 //If there was a parse failure, we have to return an error
1444 //message. It is possible that both arguments failed the parse,
1445 //but only return one in the error message.
1446 if (failure1 || failure2)
1447 {
1448 rv = Tcl_NewStringObj("arbint intadd: \"", -1);
1449 if (failure1)
1450 Tcl_AppendToObj(rv, add_arg1, -1);
1451 else
1452 Tcl_AppendToObj(rv, add_arg2, -1);
1453
1454 Tcl_AppendToObj(rv, "\" is not a recognized signed integer.", -1);
1455 Tcl_SetObjResult(interp, rv);
1456
1457 GMP_INTS_mpz_clear(&arb_arg1);
1458 GMP_INTS_mpz_clear(&arb_arg2);
1459 GMP_INTS_mpz_clear(&arb_result);
1460
1461 return(TCL_ERROR);
1462 }
1463
1464 //Calculate the sum.
1465 GMP_INTS_mpz_add(&arb_result, &arb_arg1, &arb_arg2);
1466
1467 //Figure out the number of characters required for
1468 //the output string.
1469 chars_reqd = GMP_INTS_mpz_size_in_base_10(&arb_result);
1470
1471 //Allocate space for the conversion result.
1472 string_result = TclpAlloc(sizeof(char) * chars_reqd);
1473 assert(string_result != NULL);
1474
1475 //Make the conversion to a character string.
1476 GMP_INTS_mpz_to_string(string_result, &arb_result);
1477
1478 //Assign the string result to a Tcl object.
1479 rv = Tcl_NewStringObj(string_result, -1);
1480
1481 //Deallocate the string.
1482 TclpFree(string_result);
1483
1484 //Deallocate space for the arbitrary-length integers.
1485 GMP_INTS_mpz_clear(&arb_arg1);
1486 GMP_INTS_mpz_clear(&arb_arg2);
1487 GMP_INTS_mpz_clear(&arb_result);
1488
1489 //Assign the result to be the return value.
1490 Tcl_SetObjResult(interp, rv);
1491
1492 //Return
1493 return(TCL_OK);
1494 }
1495 }
1496
1497
1498 //08/01/01: Visual inspection and some unit testing, OK.
1499 //Handles the "intcmp" subextension.
1500 static
1501 int ARBLENINTS_intcmp_handler(ClientData dummy,
1502 Tcl_Interp *interp,
1503 int objc,
1504 Tcl_Obj *objv[])
1505 {
1506 Tcl_Obj *rv;
1507
1508 //We must have two and exactly two additional arguments
1509 //to this function, which are the integers to be compared.
1510 if (objc != 4)
1511 {
1512 Tcl_WrongNumArgs(interp,
1513 2,
1514 objv,
1515 "sint sint");
1516 return(TCL_ERROR);
1517 }
1518 else
1519 {
1520 GMP_INTS_mpz_struct arb_arg1, arb_arg2;
1521 char *cmp_arg1, *cmp_arg2;
1522 int failure1, failure2;
1523 int i, j, compare_result;
1524
1525 //Allocate space for the arbitrary-length integer result.
1526 GMP_INTS_mpz_init(&arb_arg1);
1527 GMP_INTS_mpz_init(&arb_arg2);
1528
1529 //Grab pointers to the string representation of
1530 //the input arguments. The storage does not belong to us.
1531 cmp_arg1 = Tcl_GetString(objv[2]);
1532 assert(cmp_arg1 != NULL);
1533 cmp_arg2 = Tcl_GetString(objv[3]);
1534 assert(cmp_arg2 != NULL);
1535
1536 //Try to interpret either of the strings as one of the NAN tags.
1537 //We cannot compare NAN tags. If either is a NAN tag, we must signal an
1538 //error.
1539 i = GMP_INTS_identify_nan_string(cmp_arg1);
1540 j = GMP_INTS_identify_nan_string(cmp_arg2);
1541
1542 if ((i >= 0) || (j >= 0))
1543 {
1544 rv = Tcl_NewStringObj("arbint intcmp: cannot compare NAN symbolic tags.", -1);
1545 Tcl_SetObjResult(interp, rv);
1546
1547 GMP_INTS_mpz_clear(&arb_arg1);
1548 GMP_INTS_mpz_clear(&arb_arg2);
1549
1550 return(TCL_ERROR);
1551 }
1552
1553 //Try to convert both strings into arbitrary integers.
1554 GMP_INTS_mpz_set_general_int(&arb_arg1, &failure1, cmp_arg1);
1555 GMP_INTS_mpz_set_general_int(&arb_arg2, &failure2, cmp_arg2);
1556
1557 //If there was a parse failure, we have to return an error
1558 //message. It is possible that both arguments failed the parse,
1559 //but only return one in the error message.
1560 if (failure1 || failure2)
1561 {
1562 rv = Tcl_NewStringObj("arbint intcmp: \"", -1);
1563 if (failure1)
1564 Tcl_AppendToObj(rv, cmp_arg1, -1);
1565 else
1566 Tcl_AppendToObj(rv, cmp_arg2, -1);
1567
1568 Tcl_AppendToObj(rv, "\" is not a recognized signed integer.", -1);
1569 Tcl_SetObjResult(interp, rv);
1570
1571 GMP_INTS_mpz_clear(&arb_arg1);
1572 GMP_INTS_mpz_clear(&arb_arg2);
1573
1574 return(TCL_ERROR);
1575 }
1576
1577 //Calculate the compare result.
1578 compare_result = GMP_INTS_mpz_cmp(&arb_arg1, &arb_arg2);
1579
1580 //Assign the return value based on the result.
1581 if (compare_result < 0)
1582 rv = Tcl_NewStringObj("-1", -1);
1583 else if (compare_result == 0)
1584 rv = Tcl_NewStringObj("0", -1);
1585 else
1586 rv = Tcl_NewStringObj("1", -1);
1587
1588 //Deallocate space for the arbitrary-length integers.
1589 GMP_INTS_mpz_clear(&arb_arg1);
1590 GMP_INTS_mpz_clear(&arb_arg2);
1591
1592 //Assign the result to be the return value.
1593 Tcl_SetObjResult(interp, rv);
1594
1595 //Return
1596 return(TCL_OK);
1597 }
1598 }
1599
1600
1601 //Handles the "intdiv" subextension.
1602 //07/31/01: Visually inspected, OK.
1603 static
1604 int ARBLENINTS_intdiv_handler(ClientData dummy,
1605 Tcl_Interp *interp,
1606 int objc,
1607 Tcl_Obj *objv[])
1608 {
1609 Tcl_Obj *rv;
1610
1611 //We must have two and exactly two additional arguments
1612 //to this function, which are the integers whose
1613 //integer quotient is to be calculated.
1614 if (objc != 4)
1615 {
1616 Tcl_WrongNumArgs(interp,
1617 2,
1618 objv,
1619 "sint sint");
1620 return(TCL_ERROR);
1621 }
1622 else
1623 {
1624 GMP_INTS_mpz_struct arb_dividend, arb_divisor, arb_quotient, arb_remainder;
1625 char *dividend_arg1, *divisor_arg2;
1626 int failure1, failure2;
1627 unsigned chars_reqd;
1628 char *string_result;
1629 int i, j;
1630
1631 //Allocate space for the arbitrary-length integer arguments and results.
1632 GMP_INTS_mpz_init(&arb_dividend);
1633 GMP_INTS_mpz_init(&arb_divisor);
1634 GMP_INTS_mpz_init(&arb_quotient);
1635 GMP_INTS_mpz_init(&arb_remainder);
1636
1637 //Grab pointers to the string representation of
1638 //the input arguments. The storage does not belong to us.
1639 dividend_arg1 = Tcl_GetString(objv[2]);
1640 assert(dividend_arg1 != NULL);
1641 divisor_arg2 = Tcl_GetString(objv[3]);
1642 assert(divisor_arg2 != NULL);
1643
1644 //Try to interpret either of the strings as one of the NAN tags.
1645 //If it is one, return the appropriate result for
1646 //a binary operation.
1647 i = GMP_INTS_identify_nan_string(dividend_arg1);
1648 j = GMP_INTS_identify_nan_string(divisor_arg2);
1649
1650 if ((i >= 0) || (j >= 0))
1651 {
1652 const char *p;
1653
1654 //Find the max of i and j. This isn't a scientific way to tag the
1655 //result, but will be OK. Some information is lost no matter what
1656 //we do.
1657 if (i > j)
1658 ;
1659 else
1660 i = j;
1661
1662 //i now contains the max.
1663 switch (i)
1664 {
1665 case 0: p = GMP_INTS_supply_nan_string(2);
1666 break;
1667 case 1: p = GMP_INTS_supply_nan_string(3);
1668 break;
1669 case 2: p = GMP_INTS_supply_nan_string(2);
1670 break;
1671 case 3: p = GMP_INTS_supply_nan_string(3);
1672 break;
1673 default:
1674 assert(0);
1675 break;
1676 }
1677
1678 rv = Tcl_NewStringObj(p, -1);
1679 Tcl_SetObjResult(interp, rv);
1680
1681 GMP_INTS_mpz_clear(&arb_dividend);
1682 GMP_INTS_mpz_clear(&arb_divisor);
1683 GMP_INTS_mpz_clear(&arb_quotient);
1684 GMP_INTS_mpz_clear(&arb_remainder);
1685
1686 return(TCL_OK);
1687 }
1688
1689 //Try to convert both strings into arbitrary integers.
1690 GMP_INTS_mpz_set_general_int(&arb_dividend, &failure1, dividend_arg1);
1691 GMP_INTS_mpz_set_general_int(&arb_divisor, &failure2, divisor_arg2);
1692
1693 //If there was a parse failure, we have to return an error
1694 //message. It is possible that both arguments failed the parse,
1695 //but only return one in the error message.
1696 if (failure1 || failure2)
1697 {
1698 rv = Tcl_NewStringObj("arbint intdiv: \"", -1);
1699 if (failure1)
1700 Tcl_AppendToObj(rv, dividend_arg1, -1);
1701 else
1702 Tcl_AppendToObj(rv, divisor_arg2, -1);
1703
1704 Tcl_AppendToObj(rv, "\" is not a recognized signed integer.", -1);
1705 Tcl_SetObjResult(interp, rv);
1706
1707 GMP_INTS_mpz_clear(&arb_dividend);
1708 GMP_INTS_mpz_clear(&arb_divisor);
1709 GMP_INTS_mpz_clear(&arb_quotient);
1710 GMP_INTS_mpz_clear(&arb_remainder);
1711
1712 return(TCL_ERROR);
1713 }
1714
1715 //Calculate the quotient.
1716 GMP_INTS_mpz_tdiv_qr(&arb_quotient, &arb_remainder, &arb_dividend, &arb_divisor);
1717
1718 //Figure out the number of characters required for
1719 //the output string.
1720 chars_reqd = GMP_INTS_mpz_size_in_base_10(&arb_quotient);
1721
1722 //Allocate space for the conversion result.
1723 string_result = TclpAlloc(sizeof(char) * chars_reqd);
1724 assert(string_result != NULL);
1725
1726 //Make the conversion to a character string.
1727 GMP_INTS_mpz_to_string(string_result, &arb_quotient);
1728
1729 //Assign the string result to a Tcl object.
1730 rv = Tcl_NewStringObj(string_result, -1);
1731
1732 //Deallocate the string.
1733 TclpFree(string_result);
1734
1735 //Deallocate space for the arbitrary-length integers.
1736 GMP_INTS_mpz_clear(&arb_dividend);
1737 GMP_INTS_mpz_clear(&arb_divisor);
1738 GMP_INTS_mpz_clear(&arb_quotient);
1739 GMP_INTS_mpz_clear(&arb_remainder);
1740
1741 //Assign the result to be the return value.
1742 Tcl_SetObjResult(interp, rv);
1743
1744 //Return
1745 return(TCL_OK);
1746 }
1747 }
1748
1749
1750 //08/01/01: Visually inspected.
1751 //Handles the "intexp" subextension.
1752 static
1753 int ARBLENINTS_intexp_handler(ClientData dummy,
1754 Tcl_Interp *interp,
1755 int objc,
1756 Tcl_Obj *objv[])
1757 {
1758 Tcl_Obj *rv;
1759
1760 //We must have two and exactly two additional arguments
1761 //to this function, which are the integers used to
1762 //calculate the exponential.
1763 if (objc != 4)
1764 {
1765 Tcl_WrongNumArgs(interp,
1766 2,
1767 objv,
1768 "sint uint32");
1769 return(TCL_ERROR);
1770 }
1771 else
1772 {
1773 GMP_INTS_mpz_struct arb_arg1, arb_result;
1774 unsigned arg2;
1775 char *str_arg1, *str_arg2;
1776 int failure1, failure2;
1777 unsigned chars_reqd;
1778 char *string_result;
1779 int i, j;
1780
1781 //Allocate space for the arbitrary-length integers.
1782 GMP_INTS_mpz_init(&arb_arg1);
1783 GMP_INTS_mpz_init(&arb_result);
1784
1785 //Grab pointers to the string representation of
1786 //the input arguments. The storage does not belong to us.
1787 str_arg1 = Tcl_GetString(objv[2]);
1788 assert(str_arg1 != NULL);
1789 str_arg2 = Tcl_GetString(objv[3]);
1790 assert(str_arg2 != NULL);
1791
1792 //Try to interpret either of the strings as one of the NAN tags.
1793 //If it is one, return the appropriate result for
1794 //a binary operation.
1795 i = GMP_INTS_identify_nan_string(str_arg1);
1796 j = GMP_INTS_identify_nan_string(str_arg2);
1797
1798 if ((i >= 0) || (j >= 0))
1799 {
1800 const char *p;
1801
1802 //Find the max of i and j. This isn't a scientific way to tag the
1803 //result, but will be OK. Some information is lost no matter what
1804 //we do.
1805 if (i > j)
1806 ;
1807 else
1808 i = j;
1809
1810 //i now contains the max.
1811 switch (i)
1812 {
1813 case 0: p = GMP_INTS_supply_nan_string(2);
1814 break;
1815 case 1: p = GMP_INTS_supply_nan_string(3);
1816 break;
1817 case 2: p = GMP_INTS_supply_nan_string(2);
1818 break;
1819 case 3: p = GMP_INTS_supply_nan_string(3);
1820 break;
1821 default:
1822 assert(0);
1823 break;
1824 }
1825
1826 rv = Tcl_NewStringObj(p, -1);
1827 Tcl_SetObjResult(interp, rv);
1828
1829 GMP_INTS_mpz_clear(&arb_arg1);
1830 GMP_INTS_mpz_clear(&arb_result);
1831
1832 return(TCL_OK);
1833 }
1834
1835 //Try to convert the first string into arbitrary integers.
1836 //The first string can be anything, including zero or a negative
1837 //arugument.
1838 GMP_INTS_mpz_set_general_int(&arb_arg1, &failure1, str_arg1);
1839
1840 //If the conversion of the first string did not go alright,
1841 //print error message and abort.
1842 if (failure1)
1843 {
1844 rv = Tcl_NewStringObj("arbint intexp: \"", -1);
1845 Tcl_AppendToObj(rv, str_arg1, -1);
1846 Tcl_AppendToObj(rv, "\" is not a recognized signed integer.", -1);
1847 Tcl_SetObjResult(interp, rv);
1848
1849 GMP_INTS_mpz_clear(&arb_arg1);
1850 GMP_INTS_mpz_clear(&arb_result);
1851
1852 return(TCL_ERROR);
1853 }
1854
1855
1856 //Try to convert the second string into an unsigned 32-bit
1857 //integer.
1858 GMP_INTS_mpz_parse_into_uint32(&arg2, &failure2, str_arg2);
1859
1860 //If the conversion of the second string did not go alright,
1861 //print error message and abort.
1862 if (failure2)
1863 {
1864 rv = Tcl_NewStringObj("arbint intexp: \"", -1);
1865 Tcl_AppendToObj(rv, str_arg2, -1);
1866 Tcl_AppendToObj(rv, "\" is not a recognized unsigned 32-bit integer.", -1);
1867 Tcl_SetObjResult(interp, rv);
1868
1869 GMP_INTS_mpz_clear(&arb_arg1);
1870 GMP_INTS_mpz_clear(&arb_result);
1871
1872 return(TCL_ERROR);
1873 }
1874
1875 //Calculate the exponential.
1876 GMP_INTS_mpz_pow_ui(&arb_result, &arb_arg1, arg2);
1877
1878 //Figure out the number of characters required for
1879 //the output string.
1880 chars_reqd = GMP_INTS_mpz_size_in_base_10(&arb_result);
1881
1882 //Allocate space for the conversion result.
1883 string_result = TclpAlloc(sizeof(char) * chars_reqd);
1884 assert(string_result != NULL);
1885
1886 //Make the conversion to a character string.
1887 GMP_INTS_mpz_to_string(string_result, &arb_result);
1888
1889 //Assign the string result to a Tcl object.
1890 rv = Tcl_NewStringObj(string_result, -1);
1891
1892 //Deallocate the string.
1893 TclpFree(string_result);
1894
1895 //Deallocate space for the arbitrary-length integers.
1896 GMP_INTS_mpz_clear(&arb_arg1);
1897 GMP_INTS_mpz_clear(&arb_result);
1898
1899 //Assign the result to be the return value.
1900 Tcl_SetObjResult(interp, rv);
1901
1902 //Return
1903 return(TCL_OK);
1904 }
1905 }
1906
1907
1908 //Handles the "intfac" subextension.
1909 //07/29/01: Visual inspection OK. Have not located my Tcl book, am doing this
1910 //from memory an intuition as far as how to set return results and so forth.
1911 static
1912 int ARBLENINTS_intfac_handler(ClientData dummy,
1913 Tcl_Interp *interp,
1914 int objc,
1915 Tcl_Obj *objv[])
1916 {
1917 Tcl_Obj *rv;
1918
1919 //We must have one and exactly one additional argument
1920 //to this function, which is the integer whose
1921 //factorial is to be evaluated.
1922 if (objc != 3)
1923 {
1924 Tcl_WrongNumArgs(interp,
1925 2,
1926 objv,
1927 "uint32");
1928 return(TCL_ERROR);
1929 }
1930 else
1931 {
1932 GMP_INTS_mpz_struct arb_result;
1933 char *fac_arg;
1934 int failure;
1935 unsigned fac_ui_arg;
1936 unsigned chars_reqd;
1937 char *string_result;
1938 int i;
1939
1940 //Allocate space for the arbitrary-length integer result.
1941 GMP_INTS_mpz_init(&arb_result);
1942
1943 //Grab a pointer to the string representation of
1944 //the input argument. The storage does not belong to us.
1945 fac_arg = Tcl_GetString(objv[2]);
1946 assert(fac_arg != NULL);
1947
1948 //Try to interpret the string as one of the NAN tags.
1949 //If it is one, return the appropriate result for
1950 //a unary operation.
1951 if ((i = GMP_INTS_identify_nan_string(fac_arg)) >= 0)
1952 {
1953 const char *p;
1954
1955 switch (i)
1956 {
1957 case 0: p = GMP_INTS_supply_nan_string(2);
1958 break;
1959 case 1: p = GMP_INTS_supply_nan_string(3);
1960 break;
1961 case 2: p = GMP_INTS_supply_nan_string(2);
1962 break;
1963 case 3: p = GMP_INTS_supply_nan_string(3);
1964 break;
1965 default:
1966 assert(0);
1967 break;
1968 }
1969
1970 rv = Tcl_NewStringObj(p, -1);
1971 Tcl_SetObjResult(interp, rv);
1972 GMP_INTS_mpz_clear(&arb_result);
1973 return(TCL_OK);
1974 }
1975
1976 //Try to convert the string to a UINT32 using all
1977 //known methods.
1978 GMP_INTS_mpz_parse_into_uint32(&fac_ui_arg, &failure, fac_arg);
1979
1980 //If there was a parse failure, we have to return an error
1981 //message.
1982 if (failure)
1983 {
1984 rv = Tcl_NewStringObj("arbint intfac: \"", -1);
1985 Tcl_AppendToObj(rv, fac_arg, -1);
1986 Tcl_AppendToObj(rv, "\" is not a recognized 32-bit unsigned integer.", -1);
1987 Tcl_SetObjResult(interp, rv);
1988 GMP_INTS_mpz_clear(&arb_result);
1989 return(TCL_ERROR);
1990 }
1991
1992 //Calculate the factorial.
1993 GMP_INTS_mpz_fac_ui(&arb_result, fac_ui_arg);
1994
1995 //Figure out the number of characters required for
1996 //the output string.
1997 chars_reqd = GMP_INTS_mpz_size_in_base_10(&arb_result);
1998
1999 //Allocate space for the conversion result.
2000 string_result = TclpAlloc(sizeof(char) * chars_reqd);
2001 assert(string_result != NULL);
2002
2003 //Make the conversion to a character string.
2004 GMP_INTS_mpz_to_string(string_result, &arb_result);
2005
2006 //Assign the string result to a Tcl object.
2007 rv = Tcl_NewStringObj(string_result, -1);
2008
2009 //Deallocate the string.
2010 TclpFree(string_result);
2011
2012 //Deallocate space for the arbitrary-length integer.
2013 GMP_INTS_mpz_clear(&arb_result);
2014
2015 //Assign the result to be the return value.
2016 Tcl_SetObjResult(interp, rv);
2017
2018 //Return
2019 return(TCL_OK);
2020 }
2021 }
2022
2023
2024 //Handles the "intgcd" subextension.
2025 //08/06/01: Visual inspection OK.
2026 static
2027 int ARBLENINTS_intgcd_handler(ClientData dummy,
2028 Tcl_Interp *interp,
2029 int objc,
2030 Tcl_Obj *objv[])
2031 {
2032 Tcl_Obj *rv;
2033
2034 //We must have two and exactly two additional arguments
2035 //to this function, which are the integers whose
2036 //gcd is to be calculated.
2037 if (objc != 4)
2038 {
2039 Tcl_WrongNumArgs(interp,
2040 2,
2041 objv,
2042 "sint sint");
2043 return(TCL_ERROR);
2044 }
2045 else
2046 {
2047 GMP_INTS_mpz_struct arb_arg1, arb_arg2, arb_result;
2048 char *gcd_arg1, *gcd_arg2;
2049 int failure1, failure2;
2050 unsigned chars_reqd;
2051 char *string_result;
2052 int i, j;
2053
2054 //Allocate space for the arbitrary-length integer result.
2055 GMP_INTS_mpz_init(&arb_arg1);
2056 GMP_INTS_mpz_init(&arb_arg2);
2057 GMP_INTS_mpz_init(&arb_result);
2058
2059 //Grab pointers to the string representation of
2060 //the input arguments. The storage does not belong to us.
2061 gcd_arg1 = Tcl_GetString(objv[2]);
2062 assert(gcd_arg1 != NULL);
2063 gcd_arg2 = Tcl_GetString(objv[3]);
2064 assert(gcd_arg2 != NULL);
2065
2066 //Try to interpret either of the strings as one of the NAN tags.
2067 //If it is one, return the appropriate result for
2068 //a binary operation.
2069 i = GMP_INTS_identify_nan_string(gcd_arg1);
2070 j = GMP_INTS_identify_nan_string(gcd_arg2);
2071
2072 if ((i >= 0) || (j >= 0))
2073 {
2074 const char *p;
2075
2076 //Find the max of i and j. This isn't a scientific way to tag the
2077 //result, but will be OK. Some information is lost no matter what
2078 //we do.
2079 if (i > j)
2080 ;
2081 else
2082 i = j;
2083
2084 //i now contains the max.
2085 switch (i)
2086 {
2087 case 0: p = GMP_INTS_supply_nan_string(2);
2088 break;
2089 case 1: p = GMP_INTS_supply_nan_string(3);
2090 break;
2091 case 2: p = GMP_INTS_supply_nan_string(2);
2092 break;
2093 case 3: p = GMP_INTS_supply_nan_string(3);
2094 break;
2095 default:
2096 assert(0);
2097 break;
2098 }
2099
2100 rv = Tcl_NewStringObj(p, -1);
2101 Tcl_SetObjResult(interp, rv);
2102
2103 GMP_INTS_mpz_clear(&arb_arg1);
2104 GMP_INTS_mpz_clear(&arb_arg2);
2105 GMP_INTS_mpz_clear(&arb_result);
2106
2107 return(TCL_OK);
2108 }
2109
2110 //Try to convert both strings into arbitrary integers.
2111 GMP_INTS_mpz_set_general_int(&arb_arg1, &failure1, gcd_arg1);
2112 GMP_INTS_mpz_set_general_int(&arb_arg2, &failure2, gcd_arg2);
2113
2114 //If there was a parse failure, we have to return an error
2115 //message. It is possible that both arguments failed the parse,
2116 //but only return one in the error message.
2117 if (failure1 || failure2)
2118 {
2119 rv = Tcl_NewStringObj("arbint intgcd: \"", -1);
2120 if (failure1)
2121 Tcl_AppendToObj(rv, gcd_arg1, -1);
2122 else
2123 Tcl_AppendToObj(rv, gcd_arg2, -1);
2124
2125 Tcl_AppendToObj(rv, "\" is not a recognized signed integer.", -1);
2126 Tcl_SetObjResult(interp, rv);
2127
2128 GMP_INTS_mpz_clear(&arb_arg1);
2129 GMP_INTS_mpz_clear(&arb_arg2);
2130 GMP_INTS_mpz_clear(&arb_result);
2131
2132 return(TCL_ERROR);
2133 }
2134
2135 //Calculate the gcd.
2136 GMP_INTS_mpz_gcd(&arb_result, &arb_arg1, &arb_arg2);
2137
2138 //Figure out the number of characters required for
2139 //the output string.
2140 chars_reqd = GMP_INTS_mpz_size_in_base_10(&arb_result);
2141
2142 //Allocate space for the conversion result.
2143 string_result = TclpAlloc(sizeof(char) * chars_reqd);
2144 assert(string_result != NULL);
2145
2146 //Make the conversion to a character string.
2147 GMP_INTS_mpz_to_string(string_result, &arb_result);
2148
2149 //Assign the string result to a Tcl object.
2150 rv = Tcl_NewStringObj(string_result, -1);
2151
2152 //Deallocate the string.
2153 TclpFree(string_result);
2154
2155 //Deallocate space for the arbitrary-length integers.
2156 GMP_INTS_mpz_clear(&arb_arg1);
2157 GMP_INTS_mpz_clear(&arb_arg2);
2158 GMP_INTS_mpz_clear(&arb_result);
2159
2160 //Assign the result to be the return value.
2161 Tcl_SetObjResult(interp, rv);
2162
2163 //Return
2164 return(TCL_OK);
2165 }
2166 }
2167
2168
2169 //Handles the "intlcm" subextension.
2170 //08/10/01: Visual inspection OK.
2171 static
2172 int ARBLENINTS_intlcm_handler(ClientData dummy,
2173 Tcl_Interp *interp,
2174 int objc,
2175 Tcl_Obj *objv[])
2176 {
2177 Tcl_Obj *rv;
2178
2179 //We must have two and exactly two additional arguments
2180 //to this function, which are the integers whose
2181 //lcm is to be calculated.
2182 if (objc != 4)
2183 {
2184 Tcl_WrongNumArgs(interp,
2185 2,
2186 objv,
2187 "sint sint");
2188 return(TCL_ERROR);
2189 }
2190 else
2191 {
2192 GMP_INTS_mpz_struct arb_arg1, arb_arg2, gcd, remainder, arb_result;
2193 char *lcm_arg1, *lcm_arg2;
2194 int failure1, failure2;
2195 unsigned chars_reqd;
2196 char *string_result;
2197 int i, j;
2198
2199 //Allocate space for the arbitrary-length integers.
2200 GMP_INTS_mpz_init(&arb_arg1);
2201 GMP_INTS_mpz_init(&arb_arg2);
2202 GMP_INTS_mpz_init(&gcd);
2203 GMP_INTS_mpz_init(&remainder);
2204 GMP_INTS_mpz_init(&arb_result);
2205
2206 //Grab pointers to the string representation of
2207 //the input arguments. The storage does not belong to us.
2208 lcm_arg1 = Tcl_GetString(objv[2]);
2209 assert(lcm_arg1 != NULL);
2210 lcm_arg2 = Tcl_GetString(objv[3]);
2211 assert(lcm_arg2 != NULL);
2212
2213 //Try to interpret either of the strings as one of the NAN tags.
2214 //If it is one, return the appropriate result for
2215 //a binary operation.
2216 i = GMP_INTS_identify_nan_string(lcm_arg1);
2217 j = GMP_INTS_identify_nan_string(lcm_arg2);
2218
2219 if ((i >= 0) || (j >= 0))
2220 {
2221 const char *p;
2222
2223 //Find the max of i and j. This isn't a scientific way to tag the
2224 //result, but will be OK. Some information is lost no matter what
2225 //we do.
2226 if (i > j)
2227 ;
2228 else
2229 i = j;
2230
2231 //i now contains the max.
2232 switch (i)
2233 {
2234 case 0: p = GMP_INTS_supply_nan_string(2);
2235 break;
2236 case 1: p = GMP_INTS_supply_nan_string(3);
2237 break;
2238 case 2: p = GMP_INTS_supply_nan_string(2);
2239 break;
2240 case 3: p = GMP_INTS_supply_nan_string(3);
2241 break;
2242 default:
2243 assert(0);
2244 break;
2245 }
2246
2247 rv = Tcl_NewStringObj(p, -1);
2248 Tcl_SetObjResult(interp, rv);
2249
2250 GMP_INTS_mpz_clear(&arb_arg1);
2251 GMP_INTS_mpz_clear(&arb_arg2);
2252 GMP_INTS_mpz_clear(&gcd);
2253 GMP_INTS_mpz_clear(&remainder);
2254 GMP_INTS_mpz_clear(&arb_result);
2255
2256 return(TCL_OK);
2257 }
2258
2259 //Try to convert both strings into arbitrary integers.
2260 GMP_INTS_mpz_set_general_int(&arb_arg1, &failure1, lcm_arg1);
2261 GMP_INTS_mpz_set_general_int(&arb_arg2, &failure2, lcm_arg2);
2262
2263 //If there was a parse failure, we have to return an error
2264 //message. It is possible that both arguments failed the parse,
2265 //but only return one in the error message.
2266 if (failure1 || failure2)
2267 {
2268 rv = Tcl_NewStringObj("arbint intlcm: \"", -1);
2269 if (failure1)
2270 Tcl_AppendToObj(rv, lcm_arg1, -1);
2271 else
2272 Tcl_AppendToObj(rv, lcm_arg2, -1);
2273
2274 Tcl_AppendToObj(rv, "\" is not a recognized signed integer.", -1);
2275 Tcl_SetObjResult(interp, rv);
2276
2277 GMP_INTS_mpz_clear(&arb_arg1);
2278 GMP_INTS_mpz_clear(&arb_arg2);
2279 GMP_INTS_mpz_clear(&gcd);
2280 GMP_INTS_mpz_clear(&remainder);
2281 GMP_INTS_mpz_clear(&arb_result);
2282
2283 return(TCL_ERROR);
2284 }
2285
2286 //Adjust errant arguments.
2287 if (GMP_INTS_mpz_is_neg(&arb_arg1))
2288 GMP_INTS_mpz_negate(&arb_arg1);
2289 else if (GMP_INTS_mpz_is_zero(&arb_arg1))
2290 GMP_INTS_mpz_set_ui(&arb_arg1, 1);
2291 if (GMP_INTS_mpz_is_neg(&arb_arg2))
2292 GMP_INTS_mpz_negate(&arb_arg2);
2293 else if (GMP_INTS_mpz_is_zero(&arb_arg2))
2294 GMP_INTS_mpz_set_ui(&arb_arg2, 1);
2295
2296 //Calculate the gcd.
2297 GMP_INTS_mpz_gcd(&gcd, &arb_arg1, &arb_arg2);
2298
2299 //Calculate the lcm.
2300 GMP_INTS_mpz_mul(&arb_arg1, &arb_arg1, &arb_arg2);
2301 GMP_INTS_mpz_tdiv_qr(&arb_result, &remainder,
2302 &arb_arg1, &gcd);
2303
2304 //Figure out the number of characters required for
2305 //the output string.
2306 chars_reqd = GMP_INTS_mpz_size_in_base_10(&arb_result);
2307
2308 //Allocate space for the conversion result.
2309 string_result = TclpAlloc(sizeof(char) * chars_reqd);
2310 assert(string_result != NULL);
2311
2312 //Make the conversion to a character string.
2313 GMP_INTS_mpz_to_string(string_result, &arb_result);
2314
2315 //Assign the string result to a Tcl object.
2316 rv = Tcl_NewStringObj(string_result, -1);
2317
2318 //Deallocate the string.
2319 TclpFree(string_result);
2320
2321 //Deallocate space for the arbitrary-length integers.
2322 GMP_INTS_mpz_clear(&arb_arg1);
2323 GMP_INTS_mpz_clear(&arb_arg2);
2324 GMP_INTS_mpz_clear(&gcd);
2325 GMP_INTS_mpz_clear(&remainder);
2326 GMP_INTS_mpz_clear(&arb_result);
2327
2328 //Assign the result to be the return value.
2329 Tcl_SetObjResult(interp, rv);
2330
2331 //Return
2332 return(TCL_OK);
2333 }
2334 }
2335
2336
2337 //Handles the "intmod" subextension.
2338 //08/06/01: Visual inspection OK.
2339 static
2340 int ARBLENINTS_intmod_handler(ClientData dummy,
2341 Tcl_Interp *interp,
2342 int objc,
2343 Tcl_Obj *objv[])
2344 {
2345 Tcl_Obj *rv;
2346
2347 //We must have two and exactly two additional arguments
2348 //to this function, which are the integers whose
2349 //integer quotient is to be calculated.
2350 if (objc != 4)
2351 {
2352 Tcl_WrongNumArgs(interp,
2353 2,
2354 objv,
2355 "sint sint");
2356 return(TCL_ERROR);
2357 }
2358 else
2359 {
2360 GMP_INTS_mpz_struct arb_dividend, arb_divisor, arb_quotient, arb_remainder;
2361 char *dividend_arg1, *divisor_arg2;
2362 int failure1, failure2;
2363 unsigned chars_reqd;
2364 char *string_result;
2365 int i, j;
2366
2367 //Allocate space for the arbitrary-length integer arguments and results.
2368 GMP_INTS_mpz_init(&arb_dividend);
2369 GMP_INTS_mpz_init(&arb_divisor);
2370 GMP_INTS_mpz_init(&arb_quotient);
2371 GMP_INTS_mpz_init(&arb_remainder);
2372
2373 //Grab pointers to the string representation of
2374 //the input arguments. The storage does not belong to us.
2375 dividend_arg1 = Tcl_GetString(objv[2]);
2376 assert(dividend_arg1 != NULL);
2377 divisor_arg2 = Tcl_GetString(objv[3]);
2378 assert(divisor_arg2 != NULL);
2379
2380 //Try to interpret either of the strings as one of the NAN tags.
2381 //If it is one, return the appropriate result for
2382 //a binary operation.
2383 i = GMP_INTS_identify_nan_string(dividend_arg1);
2384 j = GMP_INTS_identify_nan_string(divisor_arg2);
2385
2386 if ((i >= 0) || (j >= 0))
2387 {
2388 const char *p;
2389
2390 //Find the max of i and j. This isn't a scientific way to tag the
2391 //result, but will be OK. Some information is lost no matter what
2392 //we do.
2393 if (i > j)
2394 ;
2395 else
2396 i = j;
2397
2398 //i now contains the max.
2399 switch (i)
2400 {
2401 case 0: p = GMP_INTS_supply_nan_string(2);
2402 break;
2403 case 1: p = GMP_INTS_supply_nan_string(3);
2404 break;
2405 case 2: p = GMP_INTS_supply_nan_string(2);
2406 break;
2407 case 3: p = GMP_INTS_supply_nan_string(3);
2408 break;
2409 default:
2410 assert(0);
2411 break;
2412 }
2413
2414 rv = Tcl_NewStringObj(p, -1);
2415 Tcl_SetObjResult(interp, rv);
2416
2417 GMP_INTS_mpz_clear(&arb_dividend);
2418 GMP_INTS_mpz_clear(&arb_divisor);
2419 GMP_INTS_mpz_clear(&arb_quotient);
2420 GMP_INTS_mpz_clear(&arb_remainder);
2421
2422 return(TCL_OK);
2423 }
2424
2425 //Try to convert both strings into arbitrary integers.
2426 GMP_INTS_mpz_set_general_int(&arb_dividend, &failure1, dividend_arg1);
2427 GMP_INTS_mpz_set_general_int(&arb_divisor, &failure2, divisor_arg2);
2428
2429 //If there was a parse failure, we have to return an error
2430 //message. It is possible that both arguments failed the parse,
2431 //but only return one in the error message.
2432 if (failure1 || failure2)
2433 {
2434 rv = Tcl_NewStringObj("arbint intmod: \"", -1);
2435 if (failure1)
2436 Tcl_AppendToObj(rv, dividend_arg1, -1);
2437 else
2438 Tcl_AppendToObj(rv, divisor_arg2, -1);
2439
2440 Tcl_AppendToObj(rv, "\" is not a recognized signed integer.", -1);
2441 Tcl_SetObjResult(interp, rv);
2442
2443 GMP_INTS_mpz_clear(&arb_dividend);
2444 GMP_INTS_mpz_clear(&arb_divisor);
2445 GMP_INTS_mpz_clear(&arb_quotient);
2446 GMP_INTS_mpz_clear(&arb_remainder);
2447
2448 return(TCL_ERROR);
2449 }
2450
2451 //Calculate the quotient and remainder.
2452 GMP_INTS_mpz_tdiv_qr(&arb_quotient, &arb_remainder, &arb_dividend, &arb_divisor);
2453
2454 //Figure out the number of characters required for
2455 //the output string.
2456 chars_reqd = GMP_INTS_mpz_size_in_base_10(&arb_remainder);
2457
2458 //Allocate space for the conversion result.
2459 string_result = TclpAlloc(sizeof(char) * chars_reqd);
2460 assert(string_result != NULL);
2461
2462 //Make the conversion to a character string.
2463 GMP_INTS_mpz_to_string(string_result, &arb_remainder);
2464
2465 //Assign the string result to a Tcl object.
2466 rv = Tcl_NewStringObj(string_result, -1);
2467
2468 //Deallocate the string.
2469 TclpFree(string_result);
2470
2471 //Deallocate space for the arbitrary-length integers.
2472 GMP_INTS_mpz_clear(&arb_dividend);
2473 GMP_INTS_mpz_clear(&arb_divisor);
2474 GMP_INTS_mpz_clear(&arb_quotient);
2475 GMP_INTS_mpz_clear(&arb_remainder);
2476
2477 //Assign the result to be the return value.
2478 Tcl_SetObjResult(interp, rv);
2479
2480 //Return
2481 return(TCL_OK);
2482 }
2483 }
2484
2485
2486 //Handles the "intmul" subextension.
2487 //08/06/01: Visual inspection OK.
2488 static
2489 int ARBLENINTS_intmul_handler(ClientData dummy,
2490 Tcl_Interp *interp,
2491 int objc,
2492 Tcl_Obj *objv[])
2493 {
2494 Tcl_Obj *rv;
2495
2496 //We must have two and exactly two additional arguments
2497 //to this function, which are the integers whose
2498 //product is to be calculated.
2499 if (objc != 4)
2500 {
2501 Tcl_WrongNumArgs(interp,
2502 2,
2503 objv,
2504 "sint sint");
2505 return(TCL_ERROR);
2506 }
2507 else
2508 {
2509 GMP_INTS_mpz_struct arb_arg1, arb_arg2, arb_result;
2510 char *mul_arg1, *mul_arg2;
2511 int failure1, failure2;
2512 unsigned chars_reqd;
2513 char *string_result;
2514 int i, j;
2515
2516 //Allocate space for the arbitrary-length integer result.
2517 GMP_INTS_mpz_init(&arb_arg1);
2518 GMP_INTS_mpz_init(&arb_arg2);
2519 GMP_INTS_mpz_init(&arb_result);
2520
2521 //Grab pointers to the string representation of
2522 //the input arguments. The storage does not belong to us.
2523 mul_arg1 = Tcl_GetString(objv[2]);
2524 assert(mul_arg1 != NULL);
2525 mul_arg2 = Tcl_GetString(objv[3]);
2526 assert(mul_arg2 != NULL);
2527
2528 //Try to interpret either of the strings as one of the NAN tags.
2529 //If it is one, return the appropriate result for
2530 //a binary operation.
2531 i = GMP_INTS_identify_nan_string(mul_arg1);
2532 j = GMP_INTS_identify_nan_string(mul_arg2);
2533
2534 if ((i >= 0) || (j >= 0))
2535 {
2536 const char *p;
2537
2538 //Find the max of i and j. This isn't a scientific way to tag the
2539 //result, but will be OK. Some information is lost no matter what
2540 //we do.
2541 if (i > j)
2542 ;
2543 else
2544 i = j;
2545
2546 //i now contains the max.
2547 switch (i)
2548 {
2549 case 0: p = GMP_INTS_supply_nan_string(2);
2550 break;
2551 case 1: p = GMP_INTS_supply_nan_string(3);
2552 break;
2553 case 2: p = GMP_INTS_supply_nan_string(2);
2554 break;
2555 case 3: p = GMP_INTS_supply_nan_string(3);
2556 break;
2557 default:
2558 assert(0);
2559 break;
2560 }
2561
2562 rv = Tcl_NewStringObj(p, -1);
2563 Tcl_SetObjResult(interp, rv);
2564
2565 GMP_INTS_mpz_clear(&arb_arg1);
2566 GMP_INTS_mpz_clear(&arb_arg2);
2567 GMP_INTS_mpz_clear(&arb_result);
2568
2569 return(TCL_OK);
2570 }
2571
2572 //Try to convert both strings into arbitrary integers.
2573 GMP_INTS_mpz_set_general_int(&arb_arg1, &failure1, mul_arg1);
2574 GMP_INTS_mpz_set_general_int(&arb_arg2, &failure2, mul_arg2);
2575
2576 //If there was a parse failure, we have to return an error
2577 //message. It is possible that both arguments failed the parse,
2578 //but only return one in the error message.
2579 if (failure1 || failure2)
2580 {
2581 rv = Tcl_NewStringObj("arbint intmul: \"", -1);
2582 if (failure1)
2583 Tcl_AppendToObj(rv, mul_arg1, -1);
2584 else
2585 Tcl_AppendToObj(rv, mul_arg2, -1);
2586
2587 Tcl_AppendToObj(rv, "\" is not a recognized signed integer.", -1);
2588 Tcl_SetObjResult(interp, rv);
2589
2590 GMP_INTS_mpz_clear(&arb_arg1);
2591 GMP_INTS_mpz_clear(&arb_arg2);
2592 GMP_INTS_mpz_clear(&arb_result);
2593
2594 return(TCL_ERROR);
2595 }
2596
2597 //Calculate the product.
2598 GMP_INTS_mpz_mul(&arb_result, &arb_arg1, &arb_arg2);
2599
2600 //Figure out the number of characters required for
2601 //the output string.
2602 chars_reqd = GMP_INTS_mpz_size_in_base_10(&arb_result);
2603
2604 //Allocate space for the conversion result.
2605 string_result = TclpAlloc(sizeof(char) * chars_reqd);
2606 assert(string_result != NULL);
2607
2608 //Make the conversion to a character string.
2609 GMP_INTS_mpz_to_string(string_result, &arb_result);
2610
2611 //Assign the string result to a Tcl object.
2612 rv = Tcl_NewStringObj(string_result, -1);
2613
2614 //Deallocate the string.
2615 TclpFree(string_result);
2616
2617 //Deallocate space for the arbitrary-length integers.
2618 GMP_INTS_mpz_clear(&arb_arg1);
2619 GMP_INTS_mpz_clear(&arb_arg2);
2620 GMP_INTS_mpz_clear(&arb_result);
2621
2622 //Assign the result to be the return value.
2623 Tcl_SetObjResult(interp, rv);
2624
2625 //Return
2626 return(TCL_OK);
2627 }
2628 }
2629
2630
2631 //Handles the "intsub" subextension.
2632 //08/06/01: Visual inspection OK.
2633 static
2634 int ARBLENINTS_intsub_handler(ClientData dummy,
2635 Tcl_Interp *interp,
2636 int objc,
2637 Tcl_Obj *objv[])
2638 {
2639 Tcl_Obj *rv;
2640
2641 //We must have two and exactly two additional arguments
2642 //to this function, which are the integers whose
2643 //difference is to be calculated.
2644 if (objc != 4)
2645 {
2646 Tcl_WrongNumArgs(interp,
2647 2,
2648 objv,
2649 "sint sint");
2650 return(TCL_ERROR);
2651 }
2652 else
2653 {
2654 GMP_INTS_mpz_struct arb_arg1, arb_arg2, arb_result;
2655 char *sub_arg1, *sub_arg2;
2656 int failure1, failure2;
2657 unsigned chars_reqd;
2658 char *string_result;
2659 int i, j;
2660
2661 //Allocate space for the arbitrary-length integer result.
2662 GMP_INTS_mpz_init(&arb_arg1);
2663 GMP_INTS_mpz_init(&arb_arg2);
2664 GMP_INTS_mpz_init(&arb_result);
2665
2666 //Grab pointers to the string representation of
2667 //the input arguments. The storage does not belong to us.
2668 sub_arg1 = Tcl_GetString(objv[2]);
2669 assert(sub_arg1 != NULL);
2670 sub_arg2 = Tcl_GetString(objv[3]);
2671 assert(sub_arg2 != NULL);
2672
2673 //Try to interpret either of the strings as one of the NAN tags.
2674 //If it is one, return the appropriate result for
2675 //a binary operation.
2676 i = GMP_INTS_identify_nan_string(sub_arg1);
2677 j = GMP_INTS_identify_nan_string(sub_arg2);
2678
2679 if ((i >= 0) || (j >= 0))
2680 {
2681 const char *p;
2682
2683 //Find the max of i and j. This isn't a scientific way to tag the
2684 //result, but will be OK. Some information is lost no matter what
2685 //we do.
2686 if (i > j)
2687 ;
2688 else
2689 i = j;
2690
2691 //i now contains the max.
2692 switch (i)
2693 {
2694 case 0: p = GMP_INTS_supply_nan_string(2);
2695 break;
2696 case 1: p = GMP_INTS_supply_nan_string(3);
2697 break;
2698 case 2: p = GMP_INTS_supply_nan_string(2);
2699 break;
2700 case 3: p = GMP_INTS_supply_nan_string(3);
2701 break;
2702 default:
2703 assert(0);
2704 break;
2705 }
2706
2707 rv = Tcl_NewStringObj(p, -1);
2708 Tcl_SetObjResult(interp, rv);
2709
2710 GMP_INTS_mpz_clear(&arb_arg1);
2711 GMP_INTS_mpz_clear(&arb_arg2);
2712 GMP_INTS_mpz_clear(&arb_result);
2713
2714 return(TCL_OK);
2715 }
2716
2717 //Try to convert both strings into arbitrary integers.
2718 GMP_INTS_mpz_set_general_int(&arb_arg1, &failure1, sub_arg1);
2719 GMP_INTS_mpz_set_general_int(&arb_arg2, &failure2, sub_arg2);
2720
2721 //If there was a parse failure, we have to return an error
2722 //message. It is possible that both arguments failed the parse,
2723 //but only return one in the error message.
2724 if (failure1 || failure2)
2725 {
2726 rv = Tcl_NewStringObj("arbint intsub: \"", -1);
2727 if (failure1)
2728 Tcl_AppendToObj(rv, sub_arg1, -1);
2729 else
2730 Tcl_AppendToObj(rv, sub_arg2, -1);
2731
2732 Tcl_AppendToObj(rv, "\" is not a recognized signed integer.", -1);
2733 Tcl_SetObjResult(interp, rv);
2734
2735 GMP_INTS_mpz_clear(&arb_arg1);
2736 GMP_INTS_mpz_clear(&arb_arg2);
2737 GMP_INTS_mpz_clear(&arb_result);
2738
2739 return(TCL_ERROR);
2740 }
2741
2742 //Calculate the difference.
2743 GMP_INTS_mpz_sub(&arb_result, &arb_arg1, &arb_arg2);
2744
2745 //Figure out the number of characters required for
2746 //the output string.
2747 chars_reqd = GMP_INTS_mpz_size_in_base_10(&arb_result);
2748
2749 //Allocate space for the conversion result.
2750 string_result = TclpAlloc(sizeof(char) * chars_reqd);
2751 assert(string_result != NULL);
2752
2753 //Make the conversion to a character string.
2754 GMP_INTS_mpz_to_string(string_result, &arb_result);
2755
2756 //Assign the string result to a Tcl object.
2757 rv = Tcl_NewStringObj(string_result, -1);
2758
2759 //Deallocate the string.
2760 TclpFree(string_result);
2761
2762 //Deallocate space for the arbitrary-length integers.
2763 GMP_INTS_mpz_clear(&arb_arg1);
2764 GMP_INTS_mpz_clear(&arb_arg2);
2765 GMP_INTS_mpz_clear(&arb_result);
2766
2767 //Assign the result to be the return value.
2768 Tcl_SetObjResult(interp, rv);
2769
2770 //Return
2771 return(TCL_OK);
2772 }
2773 }
2774
2775
2776 //Handles the "iseflag" subextension.
2777 //07/29/01: Visual inspection OK. Have not located my Tcl book, am doing this
2778 //from memory an intuition as far as how to set return results and so forth.
2779 static
2780 int ARBLENINTS_iseflag_handler(ClientData dummy,
2781 Tcl_Interp *interp,
2782 int objc,
2783 Tcl_Obj *objv[])
2784 {
2785 Tcl_Obj *rv;
2786
2787 //We must have one and exactly one additional argument
2788 //to this function, which is the string we want to
2789 //classify.
2790 if (objc != 3)
2791 {
2792 Tcl_WrongNumArgs(interp,
2793 2,
2794 objv,
2795 "stringarg");
2796 return(TCL_ERROR);
2797 }
2798 else
2799 {
2800 char *string_arg;
2801
2802 //Grab a pointer to the string representation of
2803 //the input argument. The storage does not belong to us.
2804 string_arg = Tcl_GetString(objv[2]);
2805 assert(string_arg != NULL);
2806
2807 //Try to parse it out. We will definitely get one of
2808 //the return values.
2809 if (!strcmp(string_arg, GMP_INTS_EF_INTOVF_POS_STRING))
2810 {
2811 rv = Tcl_NewStringObj("1", -1);
2812 }
2813 else if (!strcmp(string_arg, GMP_INTS_EF_INTOVF_NEG_STRING))
2814 {
2815 rv = Tcl_NewStringObj("2", -1);
2816 }
2817 else if (!strcmp(string_arg, GMP_INTS_EF_INTOVF_TAINT_POS_STRING))
2818 {
2819 rv = Tcl_NewStringObj("3", -1);
2820 }
2821 else if (!strcmp(string_arg, GMP_INTS_EF_INTOVF_TAINT_NEG_STRING))
2822 {
2823 rv = Tcl_NewStringObj("4", -1);
2824 }
2825 else
2826 {
2827 rv = Tcl_NewStringObj("0", -1);
2828 }
2829
2830 //Assign the result to be the return value.
2831 Tcl_SetObjResult(interp, rv);
2832
2833 //Return
2834 return(TCL_OK);
2835 }
2836 }
2837
2838
2839 //08/08/01: Visual inspection OK.
2840 static
2841 int ARBLENINTS_rnadd_handler(ClientData dummy,
2842 Tcl_Interp *interp,
2843 int objc,
2844 Tcl_Obj *objv[])
2845 {
2846 Tcl_Obj *rv;
2847
2848 //We must have exactly two additional arguments
2849 //to this function, which are the rational numbers
2850 //to add.
2851 if (objc != 4)
2852 {
2853 Tcl_WrongNumArgs(interp,
2854 2,
2855 objv,
2856 "srn srn");
2857 return(TCL_ERROR);
2858 }
2859 else
2860 {
2861 char *input_arg;
2862 int failure;
2863 char *string_result;
2864 GMP_RATS_mpq_struct arg1, arg2, result;
2865
2866 //Allocate space for the rational numbers.
2867 GMP_RATS_mpq_init(&arg1);
2868 GMP_RATS_mpq_init(&arg2);
2869 GMP_RATS_mpq_init(&result);
2870
2871 //Grab a pointer to the string representation of
2872 //the first input argument. The storage does not belong to us.
2873 input_arg = Tcl_GetString(objv[2]);
2874 assert(input_arg != NULL);
2875
2876 //Try to parse our first input string as a rational number.
2877 //If we are not successful in this, must abort.
2878 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
2879 &failure,
2880 &arg1);
2881
2882 if (failure)
2883 {
2884 rv = Tcl_NewStringObj("arbint rnadd: \"", -1);
2885 Tcl_AppendToObj(rv, input_arg, -1);
2886
2887 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
2888 Tcl_SetObjResult(interp, rv);
2889
2890 GMP_RATS_mpq_clear(&arg1);
2891 GMP_RATS_mpq_clear(&arg2);
2892 GMP_RATS_mpq_clear(&result);
2893
2894 return(TCL_ERROR);
2895 }
2896
2897 //Grab a pointer to the string representation of
2898 //the second input argument. The storage does not belong to us.
2899 input_arg = Tcl_GetString(objv[3]);
2900 assert(input_arg != NULL);
2901
2902 //Try to parse our second input string as a rational number.
2903 //If we are not successful in this, must abort.
2904 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
2905 &failure,
2906 &arg2);
2907
2908 if (failure)
2909 {
2910 rv = Tcl_NewStringObj("arbint rnadd: \"", -1);
2911 Tcl_AppendToObj(rv, input_arg, -1);
2912
2913 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
2914 Tcl_SetObjResult(interp, rv);
2915
2916 GMP_RATS_mpq_clear(&arg1);
2917 GMP_RATS_mpq_clear(&arg2);
2918 GMP_RATS_mpq_clear(&result);
2919
2920 return(TCL_ERROR);
2921 }
2922
2923 //Perform the actual addition of the rational numbers. All
2924 //error cases are covered. If either of the inputs has a
2925 //denominator of zero, this will propagate to the result.
2926 GMP_RATS_mpq_add(&result, &arg1, &arg2);
2927
2928 //If the result has been NAN'd, return the string "NAN".
2929 if (GMP_RATS_mpq_is_nan(&result))
2930 {
2931 rv = Tcl_NewStringObj("NAN", -1);
2932
2933 Tcl_SetObjResult(interp, rv);
2934
2935 GMP_RATS_mpq_clear(&arg1);
2936 GMP_RATS_mpq_clear(&arg2);
2937 GMP_RATS_mpq_clear(&result);
2938
2939 return(TCL_OK);
2940 }
2941
2942 //Allocate space for the string result which we'll form for
2943 //both numerator and denominator. We need the maximum, because we'll only
2944 //do one number at a time.
2945 string_result = TclpAlloc(sizeof(char)
2946 *
2947 INTFUNC_max
2948 (
2949 GMP_INTS_mpz_size_in_base_10(&(result.num)),
2950 GMP_INTS_mpz_size_in_base_10(&(result.den))
2951 )
2952 );
2953 assert(string_result != NULL);
2954
2955 //Convert the numerator to a string and set that to be the
2956 //return value.
2957 GMP_INTS_mpz_to_string(string_result, &(result.num));
2958 rv = Tcl_NewStringObj(string_result, -1);
2959
2960 //Append the separating slash.
2961 Tcl_AppendToObj(rv, "/", -1);
2962
2963 //Convert the denominator to a string and append that to the
2964 //return value.
2965 GMP_INTS_mpz_to_string(string_result, &(result.den));
2966 Tcl_AppendToObj(rv, string_result, -1);
2967
2968 //Assign the result to be the return value.
2969 Tcl_SetObjResult(interp, rv);
2970
2971 //Free up all dynamic memory.
2972 TclpFree(string_result);
2973 GMP_RATS_mpq_clear(&arg1);
2974 GMP_RATS_mpq_clear(&arg2);
2975 GMP_RATS_mpq_clear(&result);
2976
2977 //Return
2978 return(TCL_OK);
2979 }
2980 }
2981
2982
2983 //08/16/01: Visual inspection OK.
2984 static
2985 int ARBLENINTS_rncmp_handler(ClientData dummy,
2986 Tcl_Interp *interp,
2987 int objc,
2988 Tcl_Obj *objv[])
2989 {
2990 Tcl_Obj *rv;
2991
2992 //We must have exactly two additional arguments
2993 //to this function, which are the rational numbers
2994 //to compare.
2995 if (objc != 4)
2996 {
2997 Tcl_WrongNumArgs(interp,
2998 2,
2999 objv,
3000 "srn srn");
3001 return(TCL_ERROR);
3002 }
3003 else
3004 {
3005 char *input_arg;
3006 int failure, compare_result;
3007 GMP_RATS_mpq_struct arg1, arg2;
3008
3009 //Allocate space for the rational numbers.
3010 GMP_RATS_mpq_init(&arg1);
3011 GMP_RATS_mpq_init(&arg2);
3012
3013 //Grab a pointer to the string representation of
3014 //the first input argument. The storage does not belong to us.
3015 input_arg = Tcl_GetString(objv[2]);
3016 assert(input_arg != NULL);
3017
3018 //Try to parse our first input string as a rational number.
3019 //If we are not successful in this, must abort.
3020 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
3021 &failure,
3022 &arg1);
3023
3024 if (failure)
3025 {
3026 rv = Tcl_NewStringObj("arbint rncmp: \"", -1);
3027 Tcl_AppendToObj(rv, input_arg, -1);
3028
3029 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
3030 Tcl_SetObjResult(interp, rv);
3031
3032 GMP_RATS_mpq_clear(&arg1);
3033 GMP_RATS_mpq_clear(&arg2);
3034
3035 return(TCL_ERROR);
3036 }
3037
3038 //Grab a pointer to the string representation of
3039 //the second input argument. The storage does not belong to us.
3040 input_arg = Tcl_GetString(objv[3]);
3041 assert(input_arg != NULL);
3042
3043 //Try to parse our second input string as a rational number.
3044 //If we are not successful in this, must abort.
3045 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
3046 &failure,
3047 &arg2);
3048
3049 if (failure)
3050 {
3051 rv = Tcl_NewStringObj("arbint rncmp: \"", -1);
3052 Tcl_AppendToObj(rv, input_arg, -1);
3053
3054 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
3055 Tcl_SetObjResult(interp, rv);
3056
3057 GMP_RATS_mpq_clear(&arg1);
3058 GMP_RATS_mpq_clear(&arg2);
3059
3060 return(TCL_ERROR);
3061 }
3062
3063 //Perform the actual comparison of the rational numbers. All
3064 //error cases are covered. If either of the inputs has a
3065 //denominator of zero, this will propagate to the result.
3066 compare_result = GMP_RATS_mpq_cmp(&arg1, &arg2, &failure);
3067
3068 //If the failure flag was thrown, we have to throw an error.
3069 //The reason is that if we can't successfully compare the two
3070 //rational numbers, then we have to kill the script--logical
3071 //correctness is not possible.
3072 if (failure)
3073 {
3074 rv = Tcl_NewStringObj("arbint rncmp: can't compare supplied rational numbers.", -1);
3075
3076 Tcl_SetObjResult(interp, rv);
3077
3078 GMP_RATS_mpq_clear(&arg1);
3079 GMP_RATS_mpq_clear(&arg2);
3080
3081 return(TCL_ERROR);
3082 }
3083
3084 //Convert the comparison result to a string.
3085 if (compare_result < 0)
3086 rv = Tcl_NewStringObj("-1", -1);
3087 else if (compare_result == 0)
3088 rv = Tcl_NewStringObj("0", -1);
3089 else
3090 rv = Tcl_NewStringObj("1", -1);
3091
3092 //Assign the result to be the return value.
3093 Tcl_SetObjResult(interp, rv);
3094
3095 //Free up all dynamic memory.
3096 GMP_RATS_mpq_clear(&arg1);
3097 GMP_RATS_mpq_clear(&arg2);
3098
3099 //Return
3100 return(TCL_OK);
3101 }
3102 }
3103
3104
3105 //08/09/01: Visual inspection OK.
3106 static
3107 int ARBLENINTS_rndiv_handler(ClientData dummy,
3108 Tcl_Interp *interp,
3109 int objc,
3110 Tcl_Obj *objv[])
3111 {
3112 Tcl_Obj *rv;
3113
3114 //We must have exactly two additional arguments
3115 //to this function, which are the rational numbers
3116 //to divide.
3117 if (objc != 4)
3118 {
3119 Tcl_WrongNumArgs(interp,
3120 2,
3121 objv,
3122 "srn srn");
3123 return(TCL_ERROR);
3124 }
3125 else
3126 {
3127 char *input_arg;
3128 int failure;
3129 char *string_result;
3130 GMP_RATS_mpq_struct arg1, arg2, result;
3131
3132 //Allocate space for the rational numbers.
3133 GMP_RATS_mpq_init(&arg1);
3134 GMP_RATS_mpq_init(&arg2);
3135 GMP_RATS_mpq_init(&result);
3136
3137 //Grab a pointer to the string representation of
3138 //the first input argument. The storage does not belong to us.
3139 input_arg = Tcl_GetString(objv[2]);
3140 assert(input_arg != NULL);
3141
3142 //Try to parse our first input string as a rational number.
3143 //If we are not successful in this, must abort.
3144 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
3145 &failure,
3146 &arg1);
3147
3148 if (failure)
3149 {
3150 rv = Tcl_NewStringObj("arbint rndiv: \"", -1);
3151 Tcl_AppendToObj(rv, input_arg, -1);
3152
3153 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
3154 Tcl_SetObjResult(interp, rv);
3155
3156 GMP_RATS_mpq_clear(&arg1);
3157 GMP_RATS_mpq_clear(&arg2);
3158 GMP_RATS_mpq_clear(&result);
3159
3160 return(TCL_ERROR);
3161 }
3162
3163 //Grab a pointer to the string representation of
3164 //the second input argument. The storage does not belong to us.
3165 input_arg = Tcl_GetString(objv[3]);
3166 assert(input_arg != NULL);
3167
3168 //Try to parse our second input string as a rational number.
3169 //If we are not successful in this, must abort.
3170 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
3171 &failure,
3172 &arg2);
3173
3174 if (failure)
3175 {
3176 rv = Tcl_NewStringObj("arbint rndiv: \"", -1);
3177 Tcl_AppendToObj(rv, input_arg, -1);
3178
3179 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
3180 Tcl_SetObjResult(interp, rv);
3181
3182 GMP_RATS_mpq_clear(&arg1);
3183 GMP_RATS_mpq_clear(&arg2);
3184 GMP_RATS_mpq_clear(&result);
3185
3186 return(TCL_ERROR);
3187 }
3188
3189 //Perform the actual division of the rational numbers. All
3190 //error cases are covered. If either of the inputs has a
3191 //denominator of zero, this will propagate to the result.
3192 GMP_RATS_mpq_div(&result, &arg1, &arg2);
3193
3194 //If the result has been NAN'd, return the string "NAN".
3195 if (GMP_RATS_mpq_is_nan(&result))
3196 {
3197 rv = Tcl_NewStringObj("NAN", -1);
3198
3199 Tcl_SetObjResult(interp, rv);
3200
3201 GMP_RATS_mpq_clear(&arg1);
3202 GMP_RATS_mpq_clear(&arg2);
3203 GMP_RATS_mpq_clear(&result);
3204
3205 return(TCL_OK);
3206 }
3207
3208 //Allocate space for the string result which we'll form for
3209 //both numerator and denominator. We need the maximum, because we'll only
3210 //do one number at a time.
3211 string_result = TclpAlloc(sizeof(char)
3212 *
3213 INTFUNC_max
3214 (
3215 GMP_INTS_mpz_size_in_base_10(&(result.num)),
3216 GMP_INTS_mpz_size_in_base_10(&(result.den))
3217 )
3218 );
3219 assert(string_result != NULL);
3220
3221 //Convert the numerator to a string and set that to be the
3222 //return value.
3223 GMP_INTS_mpz_to_string(string_result, &(result.num));
3224 rv = Tcl_NewStringObj(string_result, -1);
3225
3226 //Append the separating slash.
3227 Tcl_AppendToObj(rv, "/", -1);
3228
3229 //Convert the denominator to a string and append that to the
3230 //return value.
3231 GMP_INTS_mpz_to_string(string_result, &(result.den));
3232 Tcl_AppendToObj(rv, string_result, -1);
3233
3234 //Assign the result to be the return value.
3235 Tcl_SetObjResult(interp, rv);
3236
3237 //Free up all dynamic memory.
3238 TclpFree(string_result);
3239 GMP_RATS_mpq_clear(&arg1);
3240 GMP_RATS_mpq_clear(&arg2);
3241 GMP_RATS_mpq_clear(&result);
3242
3243 //Return
3244 return(TCL_OK);
3245 }
3246 }
3247
3248
3249 //08/09/01: Visual inspection OK.
3250 static
3251 int ARBLENINTS_rnmul_handler(ClientData dummy,
3252 Tcl_Interp *interp,
3253 int objc,
3254 Tcl_Obj *objv[])
3255 {
3256 Tcl_Obj *rv;
3257
3258 //We must have exactly two additional arguments
3259 //to this function, which are the rational numbers
3260 //to add.
3261 if (objc != 4)
3262 {
3263 Tcl_WrongNumArgs(interp,
3264 2,
3265 objv,
3266 "srn srn");
3267 return(TCL_ERROR);
3268 }
3269 else
3270 {
3271 char *input_arg;
3272 int failure;
3273 char *string_result;
3274 GMP_RATS_mpq_struct arg1, arg2, result;
3275
3276 //Allocate space for the rational numbers.
3277 GMP_RATS_mpq_init(&arg1);
3278 GMP_RATS_mpq_init(&arg2);
3279 GMP_RATS_mpq_init(&result);
3280
3281 //Grab a pointer to the string representation of
3282 //the first input argument. The storage does not belong to us.
3283 input_arg = Tcl_GetString(objv[2]);
3284 assert(input_arg != NULL);
3285
3286 //Try to parse our first input string as a rational number.
3287 //If we are not successful in this, must abort.
3288 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
3289 &failure,
3290 &arg1);
3291
3292 if (failure)
3293 {
3294 rv = Tcl_NewStringObj("arbint rnmul: \"", -1);
3295 Tcl_AppendToObj(rv, input_arg, -1);
3296
3297 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
3298 Tcl_SetObjResult(interp, rv);
3299
3300 GMP_RATS_mpq_clear(&arg1);
3301 GMP_RATS_mpq_clear(&arg2);
3302 GMP_RATS_mpq_clear(&result);
3303
3304 return(TCL_ERROR);
3305 }
3306
3307 //Grab a pointer to the string representation of
3308 //the second input argument. The storage does not belong to us.
3309 input_arg = Tcl_GetString(objv[3]);
3310 assert(input_arg != NULL);
3311
3312 //Try to parse our second input string as a rational number.
3313 //If we are not successful in this, must abort.
3314 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
3315 &failure,
3316 &arg2);
3317
3318 if (failure)
3319 {
3320 rv = Tcl_NewStringObj("arbint rnmul: \"", -1);
3321 Tcl_AppendToObj(rv, input_arg, -1);
3322
3323 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
3324 Tcl_SetObjResult(interp, rv);
3325
3326 GMP_RATS_mpq_clear(&arg1);
3327 GMP_RATS_mpq_clear(&arg2);
3328 GMP_RATS_mpq_clear(&result);
3329
3330 return(TCL_ERROR);
3331 }
3332
3333 //Perform the actual multiplication of the rational numbers. All
3334 //error cases are covered. If either of the inputs has a
3335 //denominator of zero, this will propagate to the result.
3336 GMP_RATS_mpq_mul(&result, &arg1, &arg2);
3337
3338 //If the result has been NAN'd, return the string "NAN".
3339 if (GMP_RATS_mpq_is_nan(&result))
3340 {
3341 rv = Tcl_NewStringObj("NAN", -1);
3342
3343 Tcl_SetObjResult(interp, rv);
3344
3345 GMP_RATS_mpq_clear(&arg1);
3346 GMP_RATS_mpq_clear(&arg2);
3347 GMP_RATS_mpq_clear(&result);
3348
3349 return(TCL_OK);
3350 }
3351
3352 //Allocate space for the string result which we'll form for
3353 //both numerator and denominator. We need the maximum, because we'll only
3354 //do one number at a time.
3355 string_result = TclpAlloc(sizeof(char)
3356 *
3357 INTFUNC_max
3358 (
3359 GMP_INTS_mpz_size_in_base_10(&(result.num)),
3360 GMP_INTS_mpz_size_in_base_10(&(result.den))
3361 )
3362 );
3363 assert(string_result != NULL);
3364
3365 //Convert the numerator to a string and set that to be the
3366 //return value.
3367 GMP_INTS_mpz_to_string(string_result, &(result.num));
3368 rv = Tcl_NewStringObj(string_result, -1);
3369
3370 //Append the separating slash.
3371 Tcl_AppendToObj(rv, "/", -1);
3372
3373 //Convert the denominator to a string and append that to the
3374 //return value.
3375 GMP_INTS_mpz_to_string(string_result, &(result.den));
3376 Tcl_AppendToObj(rv, string_result, -1);
3377
3378 //Assign the result to be the return value.
3379 Tcl_SetObjResult(interp, rv);
3380
3381 //Free up all dynamic memory.
3382 TclpFree(string_result);
3383 GMP_RATS_mpq_clear(&arg1);
3384 GMP_RATS_mpq_clear(&arg2);
3385 GMP_RATS_mpq_clear(&result);
3386
3387 //Return
3388 return(TCL_OK);
3389 }
3390 }
3391
3392
3393 //08/09/01: Visual inspection OK.
3394 static
3395 int ARBLENINTS_rnred_handler(ClientData dummy,
3396 Tcl_Interp *interp,
3397 int objc,
3398 Tcl_Obj *objv[])
3399 {
3400 Tcl_Obj *rv;
3401
3402 //We must have exactly one additional argument
3403 //to this function, which is the rational number
3404 //to provide the fully reduced form of.
3405 if (objc != 3)
3406 {
3407 Tcl_WrongNumArgs(interp,
3408 2,
3409 objv,
3410 "srn");
3411 return(TCL_ERROR);
3412 }
3413 else
3414 {
3415 char *input_arg;
3416 int failure;
3417 char *string_result;
3418 GMP_RATS_mpq_struct rn;
3419
3420 //We will need a rational number to hold the return value
3421 //from the parsing function. Allocate that now.
3422 GMP_RATS_mpq_init(&rn);
3423
3424 //Grab a pointer to the string representation of
3425 //the input argument. The storage does not belong to us.
3426 input_arg = Tcl_GetString(objv[2]);
3427 assert(input_arg != NULL);
3428
3429 //Try to parse our input string as a rational number.
3430 //If we are not successful in this, must abort.
3431 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
3432 &failure,
3433 &rn);
3434
3435 if (failure)
3436 {
3437 rv = Tcl_NewStringObj("arbint rnred: \"", -1);
3438 Tcl_AppendToObj(rv, input_arg, -1);
3439
3440 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
3441 Tcl_SetObjResult(interp, rv);
3442
3443 GMP_RATS_mpq_clear(&rn);
3444
3445 return(TCL_ERROR);
3446 }
3447
3448 //Normalize the rational number. This takes care of the
3449 //sign and also of the coprimality of numerator and
3450 //denominator.
3451 GMP_RATS_mpq_normalize(&rn);
3452
3453 //Allocate space for the string result which we'll form for
3454 //both numbers. We need the maximum, because we'll only
3455 //do one number at a time.
3456 string_result = TclpAlloc(sizeof(char)
3457 *
3458 INTFUNC_max
3459 (
3460 GMP_INTS_mpz_size_in_base_10(&(rn.num)),
3461 GMP_INTS_mpz_size_in_base_10(&(rn.den))
3462 )
3463 );
3464 assert(string_result != NULL);
3465
3466 //Convert the numerator to a string and set that to be the
3467 //return value.
3468 GMP_INTS_mpz_to_string(string_result, &(rn.num));
3469 rv = Tcl_NewStringObj(string_result, -1);
3470
3471 //Append the separating slash.
3472 Tcl_AppendToObj(rv, "/", -1);
3473
3474 //Convert the denominator to a string and append that to the
3475 //return value.
3476 GMP_INTS_mpz_to_string(string_result, &(rn.den));
3477 Tcl_AppendToObj(rv, string_result, -1);
3478
3479 //Assign the result to be the return value.
3480 Tcl_SetObjResult(interp, rv);
3481
3482 //Free up all dynamic memory.
3483 TclpFree(string_result);
3484 GMP_RATS_mpq_clear(&rn);
3485
3486 //Return
3487 return(TCL_OK);
3488 }
3489 }
3490
3491
3492 //08/08/01: Visual inspection OK.
3493 static
3494 int ARBLENINTS_rnsub_handler(ClientData dummy,
3495 Tcl_Interp *interp,
3496 int objc,
3497 Tcl_Obj *objv[])
3498 {
3499 Tcl_Obj *rv;
3500
3501 //We must have exactly two additional arguments
3502 //to this function, which are the rational numbers
3503 //to subtract.
3504 if (objc != 4)
3505 {
3506 Tcl_WrongNumArgs(interp,
3507 2,
3508 objv,
3509 "srn srn");
3510 return(TCL_ERROR);
3511 }
3512 else
3513 {
3514 char *input_arg;
3515 int failure;
3516 char *string_result;
3517 GMP_RATS_mpq_struct arg1, arg2, result;
3518
3519 //Allocate space for the rational numbers.
3520 GMP_RATS_mpq_init(&arg1);
3521 GMP_RATS_mpq_init(&arg2);
3522 GMP_RATS_mpq_init(&result);
3523
3524 //Grab a pointer to the string representation of
3525 //the first input argument. The storage does not belong to us.
3526 input_arg = Tcl_GetString(objv[2]);
3527 assert(input_arg != NULL);
3528
3529 //Try to parse our first input string as a rational number.
3530 //If we are not successful in this, must abort.
3531 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
3532 &failure,
3533 &arg1);
3534
3535 if (failure)
3536 {
3537 rv = Tcl_NewStringObj("arbint rnsub: \"", -1);
3538 Tcl_AppendToObj(rv, input_arg, -1);
3539
3540 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
3541 Tcl_SetObjResult(interp, rv);
3542
3543 GMP_RATS_mpq_clear(&arg1);
3544 GMP_RATS_mpq_clear(&arg2);
3545 GMP_RATS_mpq_clear(&result);
3546
3547 return(TCL_ERROR);
3548 }
3549
3550 //Grab a pointer to the string representation of
3551 //the second input argument. The storage does not belong to us.
3552 input_arg = Tcl_GetString(objv[3]);
3553 assert(input_arg != NULL);
3554
3555 //Try to parse our second input string as a rational number.
3556 //If we are not successful in this, must abort.
3557 GMP_RATS_mpq_set_all_format_rat_num(input_arg,
3558 &failure,
3559 &arg2);
3560
3561 if (failure)
3562 {
3563 rv = Tcl_NewStringObj("arbint rnsub: \"", -1);
3564 Tcl_AppendToObj(rv, input_arg, -1);
3565
3566 Tcl_AppendToObj(rv, "\" is not a recognized rational number.", -1);
3567 Tcl_SetObjResult(interp, rv);
3568
3569 GMP_RATS_mpq_clear(&arg1);
3570 GMP_RATS_mpq_clear(&arg2);
3571 GMP_RATS_mpq_clear(&result);
3572
3573 return(TCL_ERROR);
3574 }
3575
3576 //Perform the actual subtraction of the rational numbers. All
3577 //error cases are covered. If either of the inputs has a
3578 //denominator of zero, this will propagate to the result.
3579 GMP_RATS_mpq_sub(&result, &arg1, &arg2);
3580
3581 //If the result has been NAN'd, return the string "NAN".
3582 if (GMP_RATS_mpq_is_nan(&result))
3583 {
3584 rv = Tcl_NewStringObj("NAN", -1);
3585
3586 Tcl_SetObjResult(interp, rv);
3587
3588 GMP_RATS_mpq_clear(&arg1);
3589 GMP_RATS_mpq_clear(&arg2);
3590 GMP_RATS_mpq_clear(&result);
3591
3592 return(TCL_OK);
3593 }
3594
3595 //Allocate space for the string result which we'll form for
3596 //both numerator and denominator. We need the maximum, because we'll only
3597 //do one number at a time.
3598 string_result = TclpAlloc(sizeof(char)
3599 *
3600 INTFUNC_max
3601 (
3602 GMP_INTS_mpz_size_in_base_10(&(result.num)),
3603 GMP_INTS_mpz_size_in_base_10(&(result.den))
3604 )
3605 );
3606 assert(string_result != NULL);
3607
3608 //Convert the numerator to a string and set that to be the
3609 //return value.
3610 GMP_INTS_mpz_to_string(string_result, &(result.num));
3611 rv = Tcl_NewStringObj(string_result, -1);
3612
3613 //Append the separating slash.
3614 Tcl_AppendToObj(rv, "/", -1);
3615
3616 //Convert the denominator to a string and append that to the
3617 //return value.
3618 GMP_INTS_mpz_to_string(string_result, &(result.den));
3619 Tcl_AppendToObj(rv, string_result, -1);
3620
3621 //Assign the result to be the return value.
3622 Tcl_SetObjResult(interp, rv);
3623
3624 //Free up all dynamic memory.
3625 TclpFree(string_result);
3626 GMP_RATS_mpq_clear(&arg1);
3627 GMP_RATS_mpq_clear(&arg2);
3628 GMP_RATS_mpq_clear(&result);
3629
3630 //Return
3631 return(TCL_OK);
3632 }
3633 }
3634
3635
3636 //This is the search data table of possible subcommands
3637 //for the "arbint" extension. These must be kept
3638 //in alphabetical order, because a binary search is done
3639 //on this table in order to find an entry. If this table
3640 //falls out of alphabetical order, the binary search may
3641 //fail when in fact the entry exists.
3642 //
3643 //In a lot of cases below, this table is set up to accept
3644 //short forms. This is purely undocumented, and I won't put
3645 //it in any documentation. In a lot of cases, these table
3646 //entries cover common mistakes where people forget the "int".
3647 //
3648 static struct EXTNINIT_subextn_bsearch_record_struct
3649 ARBLENINTS_subextn_tbl[] =
3650 {
3651 { "brap", ARBLENINTS_cfbrapab_handler },
3652 { "cfbrapab", ARBLENINTS_cfbrapab_handler },
3653 { "cfratnum", ARBLENINTS_cfratnum_handler },
3654 { "cmp", ARBLENINTS_intcmp_handler },
3655 { "commanate", ARBLENINTS_commanate_handler },
3656 { "compare", ARBLENINTS_intcmp_handler },
3657 { "const", ARBLENINTS_const_handler },
3658 { "decommanate", ARBLENINTS_decommanate_handler },
3659 { "div", ARBLENINTS_intdiv_handler },
3660 { "divide", ARBLENINTS_intdiv_handler },
3661 { "exp", ARBLENINTS_intexp_handler },
3662 { "fac", ARBLENINTS_intfac_handler },
3663 { "factorial", ARBLENINTS_intfac_handler },
3664 { "gcd", ARBLENINTS_intgcd_handler },
3665 { "intadd", ARBLENINTS_intadd_handler },
3666 { "intcmp", ARBLENINTS_intcmp_handler },
3667 { "intdiv", ARBLENINTS_intdiv_handler },
3668 { "intexp", ARBLENINTS_intexp_handler },
3669 { "intfac", ARBLENINTS_intfac_handler },
3670 { "intgcd", ARBLENINTS_intgcd_handler },
3671 { "intlcm", ARBLENINTS_intlcm_handler },
3672 { "intmod", ARBLENINTS_intmod_handler },
3673 { "intmul", ARBLENINTS_intmul_handler },
3674 { "intsub", ARBLENINTS_intsub_handler },
3675 { "iseflag", ARBLENINTS_iseflag_handler },
3676 { "lcm", ARBLENINTS_intlcm_handler },
3677 { "mod", ARBLENINTS_intmod_handler },
3678 { "mul", ARBLENINTS_intmul_handler },
3679 { "multiply", ARBLENINTS_intmul_handler },
3680 { "rnadd", ARBLENINTS_rnadd_handler },
3681 { "rncmp", ARBLENINTS_rncmp_handler },
3682 { "rndiv", ARBLENINTS_rndiv_handler },
3683 { "rnmul", ARBLENINTS_rnmul_handler },
3684 { "rnred", ARBLENINTS_rnred_handler },
3685 { "rnsub", ARBLENINTS_rnsub_handler },
3686 { "times", ARBLENINTS_intmul_handler },
3687 };
3688
3689
3690 //Procedure called when the "arbint" command is encountered in a Tcl script.
3691 //07/29/01: Visual inspection OK. Have not located my Tcl book, am doing this
3692 //from memory an intuition as far as how to set return results and so forth.
3693 int ARBLENINTS_arbint_extn_command(ClientData dummy,
3694 Tcl_Interp *interp,
3695 int objc,
3696 Tcl_Obj *objv[])
3697 {
3698 char *subcommand;
3699 //Pointer to subcommand string.
3700 int tbl_entry;
3701 //Index into the subcommand lookup table, or -1
3702 //if no match.
3703 Tcl_Obj *rv;
3704 //The return result (a string) if there is an error.
3705 //In the normal execution case, one of the functions
3706 //above supplies the return object.
3707
3708 if (objc < 2)
3709 {
3710 //It isn't possible to have an object count of less than
3711 //2, because you must have at least the command name
3712 //plus a subcommand. The best way to handle this is
3713 //to indicate wrong number of arguments.
3714 Tcl_WrongNumArgs(interp,
3715 1,
3716 objv,
3717 "option ?args?");
3718 return(TCL_ERROR);
3719 }
3720 else
3721 {
3722 //A potentially appropriate number of arguments has been
3723 //specified. Try to look up the subcommand.
3724
3725 subcommand = Tcl_GetString(objv[1]);
3726 //Grab the string representation of the subcommand.
3727 //This is constant, belongs to Tcl, and cannot be
3728 //modified.
3729
3730 tbl_entry = EXTNINIT_subextension_bsearch(
3731 ARBLENINTS_subextn_tbl,
3732 sizeof(ARBLENINTS_subextn_tbl)/sizeof(ARBLENINTS_subextn_tbl[0]),
3733 subcommand);
3734 assert(tbl_entry < (int)(sizeof(ARBLENINTS_subextn_tbl)/sizeof(ARBLENINTS_subextn_tbl[0])));
3735
3736 //If the integer returned is zero or positive, should
3737 //run the subfunction. If negative, this is an error and
3738 //should generate meaningful message. A meaningful message
3739 //would definitely consist of all valid subcommands.
3740 if (tbl_entry < 0)
3741 {
3742 //This is an error path.
3743 rv = Tcl_NewStringObj("arbint: bad option \"", -1);
3744 subcommand = Tcl_GetString(objv[1]);
3745 Tcl_AppendToObj(rv, subcommand, -1);
3746 Tcl_AppendToObj(rv, "\": valid options are ", -1);
3747
3748 for (tbl_entry=0;
3749 tbl_entry < sizeof(ARBLENINTS_subextn_tbl)/sizeof(ARBLENINTS_subextn_tbl[0]);
3750 tbl_entry++)
3751 {
3752 if ((sizeof(ARBLENINTS_subextn_tbl)/sizeof(ARBLENINTS_subextn_tbl[0]) != 1)
3753 && (tbl_entry == sizeof(ARBLENINTS_subextn_tbl)/sizeof(ARBLENINTS_subextn_tbl[0]) - 1))
3754 Tcl_AppendToObj(rv, "and ", -1);
3755 Tcl_AppendToObj(rv, ARBLENINTS_subextn_tbl[tbl_entry].name, -1);
3756 if (tbl_entry == sizeof(ARBLENINTS_subextn_tbl)/sizeof(ARBLENINTS_subextn_tbl[0]) - 1)
3757 Tcl_AppendToObj(rv, ".", -1);
3758 else
3759 Tcl_AppendToObj(rv, ", ", -1);
3760 }
3761
3762 //Now, set the return value to be the object with our
3763 //meaningful string message.
3764 Tcl_SetObjResult(interp, rv);
3765
3766 return(TCL_ERROR);
3767 }
3768 else
3769 {
3770 //Call the function pointer. Called function will
3771 //set the string return value.
3772 return((*ARBLENINTS_subextn_tbl[tbl_entry].fptr)
3773 (dummy, interp, objc, objv));
3774 }
3775 }
3776 }
3777
3778
3779 //Performs initial registration to the hash table.
3780 //07/29/01: Visual inspection OK. Have not located my Tcl book, am doing this
3781 //from memory an intuition as far as how to set return results and so forth.
3782 void ARBLENINTS_arbint_extn_init(Tcl_Interp *interp)
3783 {
3784 //Register a command named "crc32".
3785 Tcl_CreateObjCommand(interp,
3786 "arbint",
3787 (Tcl_ObjCmdProc *)ARBLENINTS_arbint_extn_command,
3788 NULL,
3789 NULL);
3790 }
3791
3792
3793
3794 //Returns version control string for file.
3795 //
3796 const char *ARBLENINTS_cvcinfo(void)
3797 {
3798 return ("$Header$");
3799 }
3800
3801
3802 //Returns version control string for associated .H file.
3803 //
3804 const char *ARBLENINTS_hvcinfo(void)
3805 {
3806 return (ARBLENINTS_H_VERSION);
3807 }
3808
3809 //End of arblenints.c.

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25