PLplot 5.15.0
Loading...
Searching...
No Matches
tclMatrix.c
Go to the documentation of this file.
1// Copyright 1994, 1995
2// Maurice LeBrun mjl@dino.ph.utexas.edu
3// Institute for Fusion Studies University of Texas at Austin
4//
5// Copyright (C) 2004 Joao Cardoso
6// Copyright (C) 2016 Alan W. Irwin
7//
8// This file is part of PLplot.
9//
10// PLplot is free software; you can redistribute it and/or modify
11// it under the terms of the GNU Library General Public License as published
12// by the Free Software Foundation; either version 2 of the License, or
13// (at your option) any later version.
14//
15// PLplot is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU Library General Public License for more details.
19//
20// You should have received a copy of the GNU Library General Public License
21// along with PLplot; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23//
24//--------------------------------------------------------------------------
25//
26// This file contains routines that implement Tcl matrices.
27// These are operators that are used to store, return, and modify
28// numeric data stored in binary array format. The emphasis is
29// on high performance and low overhead, something that Tcl lists
30// or associative arrays aren't so good at.
31//
32
33//
34//#define DEBUG
35//
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include "pldll.h"
41#include "tclMatrix.h"
42
43#if defined( TCL_MAJOR_VERSION ) && TCL_MAJOR_VERSION >= 9
44/* Tcl 9 provides Tcl_Size directly. */
45#else
46typedef int Tcl_Size;
47#endif
48
49// Cool math macros
50
51#ifndef MAX
52#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )
53#endif
54#ifndef MIN
55#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
56#endif
57
58// For the truly desperate debugging task
59
60#ifdef DEBUG_ENTER
61#define dbug_enter( a ) \
62 fprintf( stderr, "%s: Entered %s\n", __FILE__, a );
63
64#else
65#define dbug_enter( a )
66#endif
67
68// Internal data
69
70static int matTable_initted = 0; // Hash table initialization flag
71static Tcl_HashTable matTable; // Hash table for external access to data
72
73// Function prototypes
74
75// Handles matrix initialization lists
76
77static int
78MatrixAssign( Tcl_Interp* interp, tclMatrix* m,
79 int level, int *offset, int nargs, const char** args );
80
81// Invoked to process the "matrix" Tcl command.
82
83static int
84MatrixCmd( ClientData clientData, Tcl_Interp *interp, int argc, const char **argv );
85
86// Causes matrix command to be deleted.
87
88static char *
89DeleteMatrixVar( ClientData clientData,
90 Tcl_Interp *interp, char *name1, char *name2, int flags );
91
92// Releases all the resources allocated to the matrix command.
93
94static void
95DeleteMatrixCmd( ClientData clientData );
96
97// These do the put/get operations for each supported type
98
99static void
100MatrixPut_f( ClientData clientData, Tcl_Interp* interp, int index, const char *string );
101
102static void
103MatrixGet_f( ClientData clientData, Tcl_Interp* interp, int index, char *string );
104
105static void
106MatrixPut_i( ClientData clientData, Tcl_Interp* interp, int index, const char *string );
107
108static void
109MatrixGet_i( ClientData clientData, Tcl_Interp* interp, int index, char *string );
110
111//--------------------------------------------------------------------------
112//
113// Tcl_MatCmd --
114//
115// Invoked to process the "matrix" Tcl command. Creates a multiply
116// dimensioned array (matrix) of floats or ints. The number of
117// arguments determines the dimensionality.
118//
119// Results:
120// Returns the name of the new matrix.
121//
122// Side effects:
123// A new matrix (operator) gets created.
124//
125//--------------------------------------------------------------------------
126
127int
128Tcl_MatrixCmd( ClientData PL_UNUSED( clientData ), Tcl_Interp *interp,
129 int argc, const char **argv )
130{
131 register tclMatrix *matPtr;
132 int i, j, new, index, persist = 0, initializer = 0;
133 Tcl_HashEntry *hPtr;
134 Tcl_CmdInfo infoPtr;
135 char c;
136 size_t argv0_length;
137 int offset = 0;
138 size_t concatenated_argv_len;
139 char *concatenated_argv;
140 const char *const_concatenated_argv;
141
142 dbug_enter( "Tcl_MatrixCmd" );
143
144 if ( argc < 3 )
145 {
146 Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0],
147 " ?-persist? var type dim1 ?dim2? ?dim3? ...\"", (char *) NULL );
148 return TCL_ERROR;
149 }
150
151 // Create hash table on first call
152
153 if ( !matTable_initted )
154 {
156 Tcl_InitHashTable( &matTable, TCL_STRING_KEYS );
157 }
158
159 // Check for -persist flag
160
161 for ( i = 1; i < argc; i++ )
162 {
163 c = argv[i][0];
164 argv0_length = strlen( argv[i] );
165
166 // If found, set persist variable and compress argv-list
167
168 if ( ( c == '-' ) && ( strncmp( argv[i], "-persist", argv0_length ) == 0 ) )
169 {
170 persist = 1;
171 argc--;
172 for ( j = i; j < argc; j++ )
173 argv[j] = argv[j + 1];
174 break;
175 }
176 }
177
178 // Create matrix data structure
179
180 matPtr = (tclMatrix *) malloc( sizeof ( tclMatrix ) );
181 matPtr->fdata = NULL;
182 matPtr->idata = NULL;
183 matPtr->name = NULL;
184 matPtr->dim = 0;
185 matPtr->len = 1;
186 matPtr->tracing = 0;
187 matPtr->indices = NULL;
188
189 // MAX_ARRAY_DIM is #defined to be 3. Later programming logic
190 // treats all lower-dimensioned matrices as 3D matrices where the
191 // higher dimension size is 1. So must initialize all sizes
192 // to 1 here.
193 for ( i = 0; i < MAX_ARRAY_DIM; i++ )
194 matPtr->n[i] = 1;
195
196 // Create name
197 // It should be unique
198
199 argc--; argv++;
200
201 if ( Tcl_GetCommandInfo( interp, argv[0], &infoPtr ) )
202 {
203 Tcl_AppendResult( interp, "Matrix operator \"", argv[0],
204 "\" already in use", (char *) NULL );
205 free( (void *) matPtr );
206 return TCL_ERROR;
207 }
208
209 if ( Tcl_GetVar( interp, argv[0], 0 ) != NULL )
210 {
211 Tcl_AppendResult( interp, "Illegal name for Matrix operator \"",
212 argv[0], "\": local variable of same name is active",
213 (char *) NULL );
214 free( (void *) matPtr );
215 return TCL_ERROR;
216 }
217
218 matPtr->name = (char *) malloc( strlen( argv[0] ) + 1 );
219 strcpy( matPtr->name, argv[0] );
220
221 // Initialize type
222
223 argc--; argv++;
224 c = argv[0][0];
225 argv0_length = strlen( argv[0] );
226
227 if ( ( c == 'f' ) && ( strncmp( argv[0], "float", argv0_length ) == 0 ) )
228 {
229 matPtr->type = TYPE_FLOAT;
230 matPtr->put = MatrixPut_f;
231 matPtr->get = MatrixGet_f;
232 }
233 else if ( ( c == 'i' ) && ( strncmp( argv[0], "int", argv0_length ) == 0 ) )
234 {
235 matPtr->type = TYPE_INT;
236 matPtr->put = MatrixPut_i;
237 matPtr->get = MatrixGet_i;
238 }
239 else
240 {
241 Tcl_AppendResult( interp, "Matrix type \"", argv[0],
242 "\" not supported, should be \"float\" or \"int\"",
243 (char *) NULL );
244
245 DeleteMatrixCmd( (ClientData) matPtr );
246 return TCL_ERROR;
247 }
248
249 // Initialize dimensions
250
251 argc--; argv++;
252 for (; argc > 0; argc--, argv++ )
253 {
254 // Check for initializer
255
256 if ( strcmp( argv[0], "=" ) == 0 )
257 {
258 argc--; argv++;
259 initializer = 1;
260 break;
261 }
262
263 // Must be a dimensional parameter. Increment number of dimensions.
264
265 matPtr->dim++;
266 if ( matPtr->dim > MAX_ARRAY_DIM )
267 {
268 Tcl_AppendResult( interp,
269 "too many dimensions specified for Matrix operator \"",
270 matPtr->name, "\"", (char *) NULL );
271
272 DeleteMatrixCmd( (ClientData) matPtr );
273 return TCL_ERROR;
274 }
275
276 // Check to see if dimension is valid and store
277
278 index = matPtr->dim - 1;
279 matPtr->n[index] = MAX( 0, atoi( argv[0] ) );
280 matPtr->len *= matPtr->n[index];
281 }
282
283 if ( matPtr->dim < 1 )
284 {
285 Tcl_AppendResult( interp,
286 "insufficient dimensions given for Matrix operator \"",
287 matPtr->name, "\"", (char *) NULL );
288 DeleteMatrixCmd( (ClientData) matPtr );
289 return TCL_ERROR;
290 }
291
292 // Allocate space for data
293
294 switch ( matPtr->type )
295 {
296 case TYPE_FLOAT:
297 matPtr->fdata = (Mat_float *) malloc( (size_t) ( matPtr->len ) * sizeof ( Mat_float ) );
298 for ( i = 0; i < matPtr->len; i++ )
299 matPtr->fdata[i] = 0.0;
300 break;
301
302 case TYPE_INT:
303 matPtr->idata = (Mat_int *) malloc( (size_t) ( matPtr->len ) * sizeof ( Mat_int ) );
304 for ( i = 0; i < matPtr->len; i++ )
305 matPtr->idata[i] = 0;
306 break;
307 }
308
309 // Process the initializer, if present
310
311 if ( initializer )
312 {
313 if ( argc <= 0 )
314 {
315 Tcl_AppendResult( interp,
316 "no initialization data given after \"=\" for Matrix operator \"",
317 matPtr->name, "\"", (char *) NULL );
318 DeleteMatrixCmd( (ClientData) matPtr );
319 return TCL_ERROR;
320 }
321
322 // Prepare concatenated_argv string consisting of "{argv[0] argv[1] ... argv[argc-1]}"
323 // so that _any_ space-separated bunch of numerical arguments will work.
324 // Account for beginning and ending curly braces and trailing \0.
325 concatenated_argv_len = 3;
326 for ( i = 0; i < argc; i++ )
327 // Account for length of string + space separator.
328 concatenated_argv_len += strlen( argv[i] ) + 1;
329 concatenated_argv = (char *) malloc( concatenated_argv_len * sizeof ( char ) );
330
331 // Prepare for string concatenation using strcat
332 concatenated_argv[0] = '\0';
333 strcat( concatenated_argv, "{" );
334 for ( i = 0; i < argc; i++ )
335 {
336 strcat( concatenated_argv, argv[i] );
337 strcat( concatenated_argv, " " );
338 }
339 strcat( concatenated_argv, "}" );
340
341 const_concatenated_argv = (const char *) concatenated_argv;
342
343 // Use all raw indices in row-major (C) order for put in MatrixAssign
344 matPtr->nindices = matPtr->len;
345 matPtr->indices = NULL;
346
347 if ( MatrixAssign( interp, matPtr, 0, &offset, 1, &const_concatenated_argv ) != TCL_OK )
348 {
349 DeleteMatrixCmd( (ClientData) matPtr );
350 free( (void *) concatenated_argv );
351 return TCL_ERROR;
352 }
353 free( (void *) concatenated_argv );
354 }
355
356 // For later use in matrix assigments
357 // N.B. matPtr->len could be large so this check for success might
358 // be more than pro forma.
359 if ( ( matPtr->indices = (int *) malloc( (size_t) ( matPtr->len ) * sizeof ( int ) ) ) == NULL )
360 {
361 Tcl_AppendResult( interp,
362 "memory allocation failed for indices vector associated with Matrix operator \"",
363 matPtr->name, "\"", (char *) NULL );
364 DeleteMatrixCmd( (ClientData) matPtr );
365 return TCL_ERROR;
366 }
367 // Delete matrix when it goes out of scope unless -persist specified
368 // Use local variable of same name as matrix and trace it for unsets
369
370 if ( !persist )
371 {
372 if ( Tcl_SetVar( interp, matPtr->name,
373 "old_bogus_syntax_please_upgrade", 0 ) == NULL )
374 {
375 Tcl_AppendResult( interp, "unable to schedule Matrix operator \"",
376 matPtr->name, "\" for automatic deletion", (char *) NULL );
377 DeleteMatrixCmd( (ClientData) matPtr );
378 return TCL_ERROR;
379 }
380 matPtr->tracing = 1;
381 Tcl_TraceVar( interp, matPtr->name, TCL_TRACE_UNSETS,
382 (Tcl_VarTraceProc *) DeleteMatrixVar, (ClientData) matPtr );
383 }
384
385 // Create matrix operator
386
387#ifdef DEBUG
388 fprintf( stderr, "Creating Matrix operator of name %s\n", matPtr->name );
389#endif
390 Tcl_CreateCommand( interp, matPtr->name, (Tcl_CmdProc *) MatrixCmd,
391 (ClientData) matPtr, (Tcl_CmdDeleteProc *) DeleteMatrixCmd );
392
393 // Store pointer to interpreter to handle bizarre uses of multiple
394 // interpreters (e.g. as in [incr Tcl])
395
396 matPtr->interp = interp;
397
398 // Create hash table entry for this matrix operator's data
399 // This should never fail
400
401 hPtr = Tcl_CreateHashEntry( &matTable, matPtr->name, &new );
402 if ( !new )
403 {
404 Tcl_AppendResult( interp,
405 "Unable to create hash table entry for Matrix operator \"",
406 matPtr->name, "\"", (char *) NULL );
407 return TCL_ERROR;
408 }
409 Tcl_SetHashValue( hPtr, matPtr );
410
411 Tcl_SetResult( interp, matPtr->name, TCL_VOLATILE );
412 return TCL_OK;
413}
414
415//--------------------------------------------------------------------------
416//
417// Tcl_GetMatrixPtr --
418//
419// Returns a pointer to the specified matrix operator's data.
420//
421// Results:
422// None.
423//
424// Side effects:
425// None.
426//
427//--------------------------------------------------------------------------
428
429tclMatrix *
430Tcl_GetMatrixPtr( Tcl_Interp *interp, const char *matName )
431{
432 Tcl_HashEntry *hPtr;
433
434 dbug_enter( "Tcl_GetMatrixPtr" );
435
436 if ( !matTable_initted )
437 {
438 return NULL;
439 }
440
441 hPtr = Tcl_FindHashEntry( &matTable, matName );
442 if ( hPtr == NULL )
443 {
444 Tcl_AppendResult( interp, "No matrix operator named \"",
445 matName, "\"", (char *) NULL );
446 return NULL;
447 }
448 return (tclMatrix *) Tcl_GetHashValue( hPtr );
449}
450
451//--------------------------------------------------------------------------
452//
453// Tcl_MatrixInstallXtnsn --
454//
455// Install a tclMatrix extension subcommand.
456//
457// Results:
458// Should be 1. Have to think about error results.
459//
460// Side effects:
461// Enables you to install special purpose compiled code to handle
462// custom operations on a tclMatrix.
463//
464//--------------------------------------------------------------------------
465
468
469int
471{
472//
473// My goodness how I hate primitive/pathetic C. With C++ this
474// could've been as easy as:
475// List<TclMatrixXtnsnDescr> xtnlist;
476// xtnlist.append( tclMatrixXtnsnDescr(cmd,proc) );
477// grrrrr.
478//
479
481 (tclMatrixXtnsnDescr *) malloc( sizeof ( tclMatrixXtnsnDescr ) );
482
483 dbug_enter( "Tcl_MatrixInstallXtnsn" );
484
485#ifdef DEBUG
486 fprintf( stderr, "Installing a tclMatrix extension -> %s\n", cmd );
487#endif
488
489 new->cmd = malloc( strlen( cmd ) + 1 );
490 strcpy( new->cmd, cmd );
491 new->cmdproc = proc;
492 new->next = (tclMatrixXtnsnDescr *) NULL;
493
494 if ( !head )
495 {
496 tail = head = new;
497 return 1;
498 }
499 else
500 {
501 tail = tail->next = new;
502 return 1;
503 }
504}
505
506//--------------------------------------------------------------------------
507//
508// MatrixAssign --
509//
510// Assign values to the elements of a matrix.
511//
512// Returns TCL_OK on success or TC_ERROR on failure.
513//
514//--------------------------------------------------------------------------
515
516static int MatrixAssign( Tcl_Interp* interp, tclMatrix* m,
517 int level, int *offset, int nargs, const char** args )
518{
519 static int verbose = 0;
520
521 const char ** newargs;
522 Tcl_Size numnewargs;
523 int i;
524
525 if ( verbose )
526 {
527 fprintf( stderr, "level %d offset %d nargs %d\n", level, *offset, nargs );
528 for ( i = 0; i < nargs; i++ )
529 {
530 fprintf( stderr, "i = %d, args[i] = %s\n", i, args[i] );
531 }
532 }
533 // Just in case of some programming error below that creates an infinite loop
534 if ( level > 100 )
535 {
536 Tcl_AppendResult( interp, "too many list levels", (char *) NULL );
537 return TCL_ERROR;
538 }
539
540 for ( i = 0; i < nargs; i++ )
541 {
542 if ( Tcl_SplitList( interp, args[i], &numnewargs, &newargs )
543 != TCL_OK )
544 {
545 // Tcl_SplitList has already appended an error message
546 // to the result associated with interp so no need to
547 // append more.
548 return TCL_ERROR;
549 }
550
551 if ( numnewargs == 1 && strlen( args[i] ) == strlen( newargs[0] ) && strcmp( args[i], newargs[0] ) == 0 )
552 {
553 // Tcl_SplitList has gone as deep as it can go into hierarchical lists ....
554 if ( *offset >= m->nindices )
555 {
556 // Ignore any values in array assignment beyond what are needed.
557 }
558 else
559 {
560 if ( verbose )
561 fprintf( stderr, "\ta[%d] = %s\n", *offset, args[i] );
562 if ( m->indices == NULL )
563 ( m->put )( (ClientData) m, interp, *offset, args[i] );
564 else
565 ( m->put )( (ClientData) m, interp, m->indices[*offset], args[i] );
566 ( *offset )++;
567 }
568 }
569 else if ( MatrixAssign( interp, m, level + 1, offset, numnewargs, newargs )
570 != TCL_OK )
571 {
572 Tcl_Free( (char *) newargs );
573 return TCL_ERROR;
574 }
575 Tcl_Free( (char *) newargs );
576 }
577 return TCL_OK;
578}
579
580//--------------------------------------------------------------------------
581//
582// MatrixCmd --
583//
584// When a Tcl matrix command is invoked, this routine is called.
585//
586// Results:
587// A standard Tcl result value, usually TCL_OK.
588// On matrix get commands, one or a number of matrix elements are
589// printed.
590//
591// Side effects:
592// Depends on the matrix command.
593//
594//--------------------------------------------------------------------------
595
596static int
597MatrixCmd( ClientData clientData, Tcl_Interp *interp,
598 int argc, const char **argv )
599{
600 register tclMatrix *matPtr = (tclMatrix *) clientData;
601 int put = 0;
602 char c, tmp[200];
603 const char *name = argv[0];
604 // In one case (negative step and desired last actual index of 0)
605 // stop[i] is -1 so it must have an int type rather than size_t.
606 // To reduce casting most other slice-related types are also int
607 // rather than size_t.
608 int start[MAX_ARRAY_DIM], stop[MAX_ARRAY_DIM], step[MAX_ARRAY_DIM], sign_step[MAX_ARRAY_DIM];
609 int i, j, k;
610 int char_converted, change_default_start, change_default_stop;
611 size_t argv0_length;
612 // Needs dimension of 2 to contain ":" and terminating NULL as a result of sscanf calls below.
613 char c1[2], c2[2];
614
615 // Initialize
616
617 if ( argc < 2 )
618 {
619 Tcl_AppendResult( interp, "wrong # args, type: \"",
620 argv[0], " help\" for more info", (char *) NULL );
621 return TCL_ERROR;
622 }
623
624 for ( i = 0; i < MAX_ARRAY_DIM; i++ )
625 {
626 start[i] = 0;
627 stop[i] = matPtr->n[i];
628 step[i] = 1;
629 sign_step[i] = 1;
630 }
631
632 // First check for a matrix command
633
634 argc--; argv++;
635 c = argv[0][0];
636 argv0_length = strlen( argv[0] );
637
638 // dump -- send a nicely formatted listing of the array contents to stdout
639 // (very helpful for debugging)
640
641 if ( ( c == 'd' ) && ( strncmp( argv[0], "dump", argv0_length ) == 0 ) )
642 {
643 for ( i = start[0]; i < stop[0]; i++ )
644 {
645 for ( j = start[1]; j < stop[1]; j++ )
646 {
647 for ( k = start[2]; k < stop[2]; k++ )
648 {
649 ( *matPtr->get )( (ClientData) matPtr, interp, I3D( i, j, k ), tmp );
650 printf( "%s ", tmp );
651 }
652 if ( matPtr->dim > 2 )
653 printf( "\n" );
654 }
655 if ( matPtr->dim > 1 )
656 printf( "\n" );
657 }
658 printf( "\n" );
659 return TCL_OK;
660 }
661
662 // delete -- delete the array
663
664 else if ( ( c == 'd' ) && ( strncmp( argv[0], "delete", argv0_length ) == 0 ) )
665 {
666#ifdef DEBUG
667 fprintf( stderr, "Deleting array %s\n", name );
668#endif
669 Tcl_DeleteCommand( interp, name );
670 return TCL_OK;
671 }
672
673 // filter
674 // Only works on 1d matrices
675
676 else if ( ( c == 'f' ) && ( strncmp( argv[0], "filter", argv0_length ) == 0 ) )
677 {
678 Mat_float *tmpMat;
679 int ifilt, nfilt;
680
681 if ( argc != 2 )
682 {
683 Tcl_AppendResult( interp, "wrong # args: should be \"",
684 name, " ", argv[0], " num-passes\"",
685 (char *) NULL );
686 return TCL_ERROR;
687 }
688
689 if ( matPtr->dim != 1 || matPtr->type != TYPE_FLOAT )
690 {
691 Tcl_AppendResult( interp, "can only filter a 1d float matrix",
692 (char *) NULL );
693 return TCL_ERROR;
694 }
695
696 nfilt = atoi( argv[1] );
697 tmpMat = (Mat_float *) malloc( (size_t) ( matPtr->len + 2 ) * sizeof ( Mat_float ) );
698
699 for ( ifilt = 0; ifilt < nfilt; ifilt++ )
700 {
701 // Set up temporary filtering array. Use even boundary conditions.
702
703 j = 0; tmpMat[j] = matPtr->fdata[0];
704 for ( i = 0; i < matPtr->len; i++ )
705 {
706 j++; tmpMat[j] = matPtr->fdata[i];
707 }
708 j++; tmpMat[j] = matPtr->fdata[matPtr->len - 1];
709
710 // Apply 3-point binomial filter
711
712 for ( i = 0; i < matPtr->len; i++ )
713 {
714 j = i + 1;
715 matPtr->fdata[i] = 0.25 * ( tmpMat[j - 1] + 2 * tmpMat[j] + tmpMat[j + 1] );
716 }
717 }
718
719 free( (void *) tmpMat );
720 return TCL_OK;
721 }
722
723 // help
724
725 else if ( ( c == 'h' ) && ( strncmp( argv[0], "help", argv0_length ) == 0 ) )
726 {
727 Tcl_AppendResult( interp,
728 "Available subcommands:\n\
729dump - return the values in the matrix as a string\n\
730delete - delete the matrix (including the matrix command)\n\
731filter - apply a three-point averaging (with a number of passes; ome-dimensional only)\n\
732help - this information\n\
733info - return the dimensions\n\
734max - return the maximum value for the entire matrix or for the first N entries\n\
735min - return the minimum value for the entire matrix or for the first N entries\n\
736redim - resize the matrix (for one-dimensional matrices only)\n\
737scale - scale the values by a given factor (for one-dimensional matrices only)\n\
738\n\
739Set and get values:\n\
740matrix m f 3 3 3 - define matrix command \"m\", three-dimensional, floating-point data\n\
741m 1 2 3 - return the value of matrix element [1,2,3]\n\
742m 1 2 3 = 2.0 - set the value of matrix element [1,2,3] to 2.0 (do not return the value)\n\
743m * 2 3 = 2.0 - set a slice consisting of all elements with second index 2 and third index 3 to 2.0",
744 (char *) NULL );
745 return TCL_OK;
746 }
747
748 // info
749
750 else if ( ( c == 'i' ) && ( strncmp( argv[0], "info", argv0_length ) == 0 ) )
751 {
752 for ( i = 0; i < matPtr->dim; i++ )
753 {
754 sprintf( tmp, "%d", matPtr->n[i] );
755 // Must avoid trailing space.
756 if ( i < matPtr->dim - 1 )
757 Tcl_AppendResult( interp, tmp, " ", (char *) NULL );
758 else
759 Tcl_AppendResult( interp, tmp, (char *) NULL );
760 }
761 return TCL_OK;
762 }
763
764 // max
765
766 else if ( ( c == 'm' ) && ( strncmp( argv[0], "max", argv0_length ) == 0 ) )
767 {
768 int len;
769 if ( argc < 1 || argc > 2 )
770 {
771 Tcl_AppendResult( interp, "wrong # args: should be \"",
772 name, " ", argv[0], " ?length?\"",
773 (char *) NULL );
774 return TCL_ERROR;
775 }
776
777 if ( argc == 2 )
778 {
779 len = atoi( argv[1] );
780 if ( len < 0 || len > matPtr->len )
781 {
782 Tcl_AppendResult( interp, "specified length out of valid range",
783 (char *) NULL );
784 return TCL_ERROR;
785 }
786 }
787 else
788 len = matPtr->len;
789
790 if ( len == 0 )
791 {
792 Tcl_AppendResult( interp, "attempt to find maximum of array with zero elements",
793 (char *) NULL );
794 return TCL_ERROR;
795 }
796
797 switch ( matPtr->type )
798 {
799 case TYPE_FLOAT: {
800 Mat_float max = matPtr->fdata[0];
801 for ( i = 1; i < len; i++ )
802 max = MAX( max, matPtr->fdata[i] );
803 //sprintf(tmp, "%.17g", max);
804 Tcl_PrintDouble( interp, max, tmp );
805 Tcl_AppendResult( interp, tmp, (char *) NULL );
806 break;
807 }
808 case TYPE_INT: {
809 Mat_int max = matPtr->idata[0];
810 for ( i = 1; i < len; i++ )
811 max = MAX( max, matPtr->idata[i] );
812 sprintf( tmp, "%d", max );
813 Tcl_AppendResult( interp, tmp, (char *) NULL );
814 break;
815 }
816 }
817 return TCL_OK;
818 }
819
820 // min
821
822 else if ( ( c == 'm' ) && ( strncmp( argv[0], "min", argv0_length ) == 0 ) )
823 {
824 int len;
825 if ( argc < 1 || argc > 2 )
826 {
827 Tcl_AppendResult( interp, "wrong # args: should be \"",
828 name, " ", argv[0], " ?length?\"",
829 (char *) NULL );
830 return TCL_ERROR;
831 }
832
833 if ( argc == 2 )
834 {
835 len = atoi( argv[1] );
836 if ( len < 0 || len > matPtr->len )
837 {
838 Tcl_AppendResult( interp, "specified length out of valid range",
839 (char *) NULL );
840 return TCL_ERROR;
841 }
842 }
843 else
844 len = matPtr->len;
845
846 if ( len == 0 )
847 {
848 Tcl_AppendResult( interp, "attempt to find minimum of array with zero elements",
849 (char *) NULL );
850 return TCL_ERROR;
851 }
852
853 switch ( matPtr->type )
854 {
855 case TYPE_FLOAT: {
856 Mat_float min = matPtr->fdata[0];
857 for ( i = 1; i < len; i++ )
858 min = MIN( min, matPtr->fdata[i] );
859 //sprintf(tmp, "%.17g", min);
860 Tcl_PrintDouble( interp, min, tmp );
861 Tcl_AppendResult( interp, tmp, (char *) NULL );
862 break;
863 }
864 case TYPE_INT: {
865 Mat_int min = matPtr->idata[0];
866 for ( i = 1; i < len; i++ )
867 min = MIN( min, matPtr->idata[i] );
868 sprintf( tmp, "%d", min );
869 Tcl_AppendResult( interp, tmp, (char *) NULL );
870 break;
871 }
872 }
873 return TCL_OK;
874 }
875
876 // redim
877 // Only works on 1d matrices
878
879 else if ( ( c == 'r' ) && ( strncmp( argv[0], "redim", argv0_length ) == 0 ) )
880 {
881 int newlen;
882 void *data;
883
884 if ( argc != 2 )
885 {
886 Tcl_AppendResult( interp, "wrong # args: should be \"",
887 name, " ", argv[0], " length\"",
888 (char *) NULL );
889 return TCL_ERROR;
890 }
891
892 if ( matPtr->dim != 1 )
893 {
894 Tcl_AppendResult( interp, "can only redim a 1d matrix",
895 (char *) NULL );
896 return TCL_ERROR;
897 }
898
899 newlen = atoi( argv[1] );
900 switch ( matPtr->type )
901 {
902 case TYPE_FLOAT:
903 data = realloc( matPtr->fdata, (size_t) newlen * sizeof ( Mat_float ) );
904 if ( newlen != 0 && data == NULL )
905 {
906 Tcl_AppendResult( interp, "redim failed!",
907 (char *) NULL );
908 return TCL_ERROR;
909 }
910 matPtr->fdata = (Mat_float *) data;
911 for ( i = matPtr->len; i < newlen; i++ )
912 matPtr->fdata[i] = 0.0;
913 break;
914
915 case TYPE_INT:
916 data = realloc( matPtr->idata, (size_t) newlen * sizeof ( Mat_int ) );
917 if ( newlen != 0 && data == NULL )
918 {
919 Tcl_AppendResult( interp, "redim failed!",
920 (char *) NULL );
921 return TCL_ERROR;
922 }
923 matPtr->idata = (Mat_int *) data;
924 for ( i = matPtr->len; i < newlen; i++ )
925 matPtr->idata[i] = 0;
926 break;
927 }
928 matPtr->n[0] = matPtr->len = newlen;
929 // For later use in matrix assigments
930 // N.B. matPtr->len could be large so this check for success might
931 // be more than pro forma.
932 data = realloc( matPtr->indices, (size_t) ( matPtr->len ) * sizeof ( int ) );
933 if ( newlen != 0 && data == NULL )
934 {
935 Tcl_AppendResult( interp, "redim failed!", (char *) NULL );
936 return TCL_ERROR;
937 }
938 matPtr->indices = (int *) data;
939 return TCL_OK;
940 }
941
942 // scale
943 // Only works on 1d matrices
944
945 else if ( ( c == 's' ) && ( strncmp( argv[0], "scale", argv0_length ) == 0 ) )
946 {
947 Mat_float scale;
948
949 if ( argc != 2 )
950 {
951 Tcl_AppendResult( interp, "wrong # args: should be \"",
952 name, " ", argv[0], " scale-factor\"",
953 (char *) NULL );
954 return TCL_ERROR;
955 }
956
957 if ( matPtr->dim != 1 )
958 {
959 Tcl_AppendResult( interp, "can only scale a 1d matrix",
960 (char *) NULL );
961 return TCL_ERROR;
962 }
963
964 scale = atof( argv[1] );
965 switch ( matPtr->type )
966 {
967 case TYPE_FLOAT:
968 for ( i = 0; i < matPtr->len; i++ )
969 matPtr->fdata[i] *= scale;
970 break;
971
972 case TYPE_INT:
973 for ( i = 0; i < matPtr->len; i++ )
974 matPtr->idata[i] = (Mat_int) ( (Mat_float) ( matPtr->idata[i] ) * scale );
975 break;
976 }
977 return TCL_OK;
978 }
979
980 // Not a "standard" command, check the extension commands.
981
982 {
984 for (; p; p = p->next )
985 {
986 if ( ( c == p->cmd[0] ) && ( strncmp( argv[0], p->cmd, argv0_length ) == 0 ) )
987 {
988#ifdef DEBUG
989 fprintf( stderr, "found a match, invoking %s\n", p->cmd );
990#endif
991 return ( *( p->cmdproc ) )( matPtr, interp, --argc, ++argv );
992 }
993 }
994 }
995
996 // Must be a put or get of an array slice or array value.
997
998 // Determine array index slice adopting the same rules as the Python case
999 // documented at <https://docs.python.org/3/library/stdtypes.html#common-sequence-operations>
1000 // Also, for the case where just a _single_ ":" is used to represent the
1001 // complete range of indices for a dimension, the
1002 // notation "*" can be used as well for backwards compatibility
1003 // with the limited slice capability that was available before
1004 // this full slice capability was implemented.
1005
1006 if ( argc < matPtr->dim )
1007 {
1008 Tcl_AppendResult( interp, "not enough dimensions specified for \"",
1009 name, "\"", (char *) NULL );
1010 return TCL_ERROR;
1011 }
1012
1013 for ( i = 0; i < matPtr->dim; i++ )
1014 {
1015 // Because of argc and argv initialization and logic at end of
1016 // loop which decrements argc and increments argv, argv[0]
1017 // walks through the space-separated command-line strings that
1018 // have been parsed by Tcl for each iteration of this loop.
1019 // N.B. argv[0] should point to valid memory (i.e., one of the
1020 // command-line strings) because of the above initial argc
1021 // check and loop limits.
1022 argv0_length = strlen( argv[0] );
1023 // According to Linux man page for sscanf, a straightforward interpretation of the C standard
1024 // indicates that %n should not be counted as a successful conversion when calculating
1025 // the sscanf return value, but that man page also says should not count on that in general.
1026 // So in the logic below use the ">= " test to allow for both possibilities.
1027
1028 // Default values if not determined below.
1029 start[i] = 0;
1030 stop[i] = matPtr->n[i];
1031 step[i] = 1;
1032 change_default_start = 0;
1033 change_default_stop = 0;
1034 // i:j:k
1035 if ( sscanf( argv[0], "%d%1[:]%d%1[:]%d%n", start + i, c1, stop + i, c2, step + i, &char_converted ) >= 5 )
1036 {
1037 }
1038 // i:j:
1039 else if ( sscanf( argv[0], "%d%1[:]%d%1[:]%n", start + i, c1, stop + i, c2, &char_converted ) >= 4 )
1040 {
1041 }
1042 // i:j
1043 else if ( sscanf( argv[0], "%d%1[:]%d%n", start + i, c1, stop + i, &char_converted ) >= 3 )
1044 {
1045 }
1046 // i::k
1047 else if ( sscanf( argv[0], "%d%1[:]%1[:]%d%n", start + i, c1, c2, step + i, &char_converted ) >= 4 )
1048 {
1049 if ( step[i] < 0 )
1050 {
1051 change_default_stop = 1;
1052 }
1053 }
1054 // i::
1055 else if ( sscanf( argv[0], "%d%1[:]%1[:]%n", start + i, c1, c2, &char_converted ) >= 3 )
1056 {
1057 }
1058 // i:
1059 else if ( sscanf( argv[0], "%d%1[:]%n", start + i, c1, &char_converted ) >= 2 )
1060 {
1061 }
1062 // :j:k
1063 else if ( sscanf( argv[0], "%1[:]%d%1[:]%d%n", c1, stop + i, c2, step + i, &char_converted ) >= 4 )
1064 {
1065 if ( step[i] < 0 )
1066 {
1067 change_default_start = 1;
1068 }
1069 }
1070 // :j:
1071 else if ( sscanf( argv[0], "%1[:]%d%1[:]%n", c1, stop + i, c2, &char_converted ) >= 3 )
1072 {
1073 }
1074 // :j
1075 else if ( sscanf( argv[0], "%1[:]%d%n", c1, stop + i, &char_converted ) >= 2 )
1076 {
1077 }
1078 // ::k
1079 else if ( sscanf( argv[0], "%1[:]%1[:]%d%n", c1, c2, step + i, &char_converted ) >= 3 )
1080 {
1081 if ( step[i] < 0 )
1082 {
1083 change_default_start = 1;
1084 change_default_stop = 1;
1085 }
1086 }
1087 // ::
1088 else if ( strcmp( argv[0], "::" ) == 0 )
1089 char_converted = 2;
1090 // :
1091 else if ( strcmp( argv[0], ":" ) == 0 )
1092 char_converted = 1;
1093 // *
1094 else if ( strcmp( argv[0], "*" ) == 0 )
1095 char_converted = 1;
1096 // i
1097 else if ( sscanf( argv[0], "%d%n", start + i, &char_converted ) >= 1 )
1098 {
1099 // Special checks for the pure index case (just like in Python).
1100 if ( start[i] < 0 )
1101 start[i] += matPtr->n[i];
1102 if ( start[i] < 0 || start[i] > matPtr->n[i] - 1 )
1103 {
1104 sprintf( tmp, "Array index %d out of bounds: original string = \"%s\"; transformed = %d; min = 0; max = %d\n",
1105 i, argv[0], start[i], matPtr->n[i] - 1 );
1106 Tcl_AppendResult( interp, tmp, (char *) NULL );
1107 return TCL_ERROR;
1108 }
1109 stop[i] = start[i] + 1;
1110 }
1111 else
1112 {
1113 sprintf( tmp, "Array slice for index %d with original string = \"%s\" could not be parsed\n",
1114 i, argv[0] );
1115 Tcl_AppendResult( interp, tmp, (char *) NULL );
1116 return TCL_ERROR;
1117 }
1118
1119 // Check, convert and sanitize start[i], stop[i], and step[i] values.
1120 if ( step[i] == 0 )
1121 {
1122 Tcl_AppendResult( interp, "step part of slice must be non-zero",
1123 (char *) NULL );
1124 return TCL_ERROR;
1125 }
1126 sign_step[i] = ( step[i] > 0 ) ? 1 : -1;
1127 if ( (size_t) char_converted > argv0_length )
1128 {
1129 Tcl_AppendResult( interp, "MatrixCmd, internal logic error",
1130 (char *) NULL );
1131 return TCL_ERROR;
1132 }
1133 if ( (size_t) char_converted < argv0_length )
1134 {
1135 sprintf( tmp, "Array slice for index %d with original string = \"%s\" "
1136 "had trailing unparsed characters\n", i, argv[0] );
1137 Tcl_AppendResult( interp, tmp, (char *) NULL );
1138 return TCL_ERROR;
1139 }
1140 if ( start[i] < 0 )
1141 start[i] += matPtr->n[i];
1142 start[i] = MAX( 0, MIN( matPtr->n[i] - 1, start[i] ) );
1143 if ( change_default_start )
1144 start[i] = matPtr->n[i] - 1;
1145 if ( stop[i] < 0 )
1146 stop[i] += matPtr->n[i];
1147 if ( step[i] > 0 )
1148 stop[i] = MAX( 0, MIN( matPtr->n[i], stop[i] ) );
1149 else
1150 stop[i] = MAX( -1, MIN( matPtr->n[i], stop[i] ) );
1151 if ( change_default_stop )
1152 stop[i] = -1;
1153
1154 // At this stage, start, stop, and step (!=0), correspond to
1155 // i, j, and k (!=0) in the slice documentation given at
1156 // <https://docs.python.org/3/library/stdtypes.html#common-sequence-operations>.
1157 // with all checks and conversions made. According to note 5
1158 // of that documentation (translated to the present start,
1159 // stop and step notation and also subject to the clarifying
1160 // discussion in <http://bugs.python.org/issue28614>) the
1161 // array index should take on the values
1162 // index = start + n*step
1163 // where n 0, 1, etc., with that sequence
1164 // terminated just before index = stop is reached.
1165 // Therefore, the for loop for a typical index when step is positive should read
1166 // for ( i = start[0]; i < stop[0]; i += step[0] )
1167 // and when step is negative should read
1168 // for ( i = start[0]; i > stop[0]; i += step[0] )
1169 // So to cover both cases, we use for loops of the
1170 // following form below
1171 // for ( i = start[0]; sign_step[0]*i < stop[0]; i += step[0] )
1172 // where stop has been transformed as follows:
1173#ifdef DEBUG
1174 fprintf( stderr, "Array slice for index %d with original string = \"%s\" "
1175 "yielded start[i], stop[i], transformed stop[i], and step[i] = "
1176 "%d, %d, ", i, argv[0], start[i], stop[i] );
1177#endif
1178 stop[i] = sign_step[i] * stop[i];
1179#ifdef DEBUG
1180 fprintf( stderr, "%d, %d\n", stop[i], step[i] );
1181#endif
1182 argc--; argv++;
1183 }
1184
1185 // If there is an "=" after indices, it's a put. Do error checking.
1186
1187 if ( argc > 0 )
1188 {
1189 put = 1;
1190 if ( strcmp( argv[0], "=" ) == 0 )
1191 {
1192 argc--; argv++;
1193 if ( argc == 0 )
1194 {
1195 Tcl_AppendResult( interp, "no value specified",
1196 (char *) NULL );
1197 return TCL_ERROR;
1198 }
1199 }
1200 else
1201 {
1202 Tcl_AppendResult( interp, "extra characters after indices: \"",
1203 argv[0], "\"", (char *) NULL );
1204 return TCL_ERROR;
1205 }
1206 }
1207
1208 // Calculate which indices will be used for the given index slices.
1209 matPtr->nindices = 0;
1210
1211 for ( i = start[0]; sign_step[0] * i < stop[0]; i += step[0] )
1212 {
1213 for ( j = start[1]; sign_step[1] * j < stop[1]; j += step[1] )
1214 {
1215 for ( k = start[2]; sign_step[2] * k < stop[2]; k += step[2] )
1216 {
1217 matPtr->indices[matPtr->nindices++] = I3D( i, j, k );
1218 }
1219 }
1220 }
1221
1222 // Do the get/put.
1223 // The loop over all elements takes care of the multi-element cases.
1224 if ( put )
1225 {
1226 char *endptr;
1227 // Check whether argv[0] could be interpreted as a raw single
1228 // number with no trailing characters.
1229 switch ( matPtr->type )
1230 {
1231 case TYPE_FLOAT:
1232 strtod( argv[0], &endptr );
1233 break;
1234 case TYPE_INT:
1235 strtol( argv[0], &endptr, 10 );
1236 break;
1237 }
1238 if ( argc == 1 && *argv[0] != '\0' && *endptr == '\0' )
1239 {
1240 // If _all_ characters of single RHS string can be
1241 // successfully read as a single number, then assign all
1242 // matrix elements with indices in matPtr->indices to that
1243 // single number.
1244 for ( i = 0; i < matPtr->nindices; i++ )
1245 ( *matPtr->put )( (ClientData) matPtr, interp, matPtr->indices[i], argv[0] );
1246 }
1247 else
1248 {
1249 // If RHS cannot be successfully read as a single number,
1250 // then assume it is a collection of numbers (in list form
1251 // or white-space separated). Concatenate all remaining
1252 // elements of argv into list form, then use MatrixAssign
1253 // to assign all matrix elements with indices in
1254 // matPtr->indices using all (deep) non-list elements of
1255 // that list.
1256 int offset = 0;
1257 size_t concatenated_argv_len;
1258 char *concatenated_argv;
1259 const char *const_concatenated_argv;
1260
1261 // Prepare concatenated_argv string consisting of
1262 // "{argv[0] argv[1] ... argv[argc-1]}" so that _any_
1263 // space-separated bunch of numerical arguments or lists
1264 // of those will work. Account for beginning and ending
1265 // curly braces and trailing \0.
1266 concatenated_argv_len = 3;
1267 for ( i = 0; i < argc; i++ )
1268 // Account for length of string + space separator.
1269 concatenated_argv_len += strlen( argv[i] ) + 1;
1270 concatenated_argv = (char *) malloc( concatenated_argv_len * sizeof ( char ) );
1271
1272 // Prepare for string concatenation using strcat
1273 concatenated_argv[0] = '\0';
1274 strcat( concatenated_argv, "{" );
1275 for ( i = 0; i < argc; i++ )
1276 {
1277 strcat( concatenated_argv, argv[i] );
1278 strcat( concatenated_argv, " " );
1279 }
1280 strcat( concatenated_argv, "}" );
1281
1282 const_concatenated_argv = (const char *) concatenated_argv;
1283
1284 // Assign matrix elements using all numbers collected from
1285 // the potentially deep list, const_concatenated_argv.
1286 if ( MatrixAssign( interp, matPtr, 0, &offset, 1, &const_concatenated_argv ) != TCL_OK )
1287 {
1288 free( (void *) concatenated_argv );
1289 return TCL_ERROR;
1290 }
1291 free( (void *) concatenated_argv );
1292 }
1293 }
1294 else
1295 {
1296 // get
1297 for ( i = 0; i < matPtr->nindices; i++ )
1298 {
1299 ( *matPtr->get )( (ClientData) matPtr, interp, matPtr->indices[i], tmp );
1300 if ( i < matPtr->nindices - 1 )
1301 Tcl_AppendResult( interp, tmp, " ", (char *) NULL );
1302 else
1303 Tcl_AppendResult( interp, tmp, (char *) NULL );
1304 }
1305 }
1306
1307 return TCL_OK;
1308}
1309
1310//--------------------------------------------------------------------------
1311//
1312// Routines to handle Matrix get/put dependent on type:
1313//
1314// MatrixPut_f MatrixGet_f
1315// MatrixPut_i MatrixGet_i
1316//
1317// A "put" converts from string format to the intrinsic type, storing into
1318// the array.
1319//
1320// A "get" converts from the intrinsic type to string format, storing into
1321// a string buffer.
1322//
1323//--------------------------------------------------------------------------
1324
1325static void
1326MatrixPut_f( ClientData clientData, Tcl_Interp* PL_UNUSED( interp ), int index, const char *string )
1327{
1328 tclMatrix *matPtr = (tclMatrix *) clientData;
1329
1330 matPtr->fdata[index] = atof( string );
1331}
1332
1333static void
1334MatrixGet_f( ClientData clientData, Tcl_Interp* interp, int index, char *string )
1335{
1336 tclMatrix *matPtr = (tclMatrix *) clientData;
1337 double value = matPtr->fdata[index];
1338
1339 //sprintf(string, "%.17g", value);
1340 Tcl_PrintDouble( interp, value, string );
1341}
1342
1343static void
1344MatrixPut_i( ClientData clientData, Tcl_Interp* PL_UNUSED( interp ), int index, const char *string )
1345{
1346 tclMatrix *matPtr = (tclMatrix *) clientData;
1347
1348 if ( ( strlen( string ) > 2 ) && ( strncmp( string, "0x", 2 ) == 0 ) )
1349 {
1350 matPtr->idata[index] = (Mat_int) strtoul( &string[2], NULL, 16 );
1351 }
1352 else
1353 matPtr->idata[index] = atoi( string );
1354}
1355
1356static void
1357MatrixGet_i( ClientData clientData, Tcl_Interp* PL_UNUSED( interp ), int index, char *string )
1358{
1359 tclMatrix *matPtr = (tclMatrix *) clientData;
1360
1361 sprintf( string, "%d", matPtr->idata[index] );
1362}
1363
1364//--------------------------------------------------------------------------
1365//
1366// DeleteMatrixVar --
1367//
1368// Causes matrix command to be deleted. Invoked when variable
1369// associated with matrix command is unset.
1370//
1371// Results:
1372// None.
1373//
1374// Side effects:
1375// See DeleteMatrixCmd.
1376//
1377//--------------------------------------------------------------------------
1378
1379static char *
1380DeleteMatrixVar( ClientData clientData,
1381 Tcl_Interp * PL_UNUSED( interp ), char * PL_UNUSED( name1 ), char * PL_UNUSED( name2 ), int PL_UNUSED( flags ) )
1382{
1383 tclMatrix *matPtr = (tclMatrix *) clientData;
1384 Tcl_CmdInfo infoPtr;
1385 char *name;
1386
1387 dbug_enter( "DeleteMatrixVar" );
1388
1389 if ( matPtr->tracing != 0 )
1390 {
1391 matPtr->tracing = 0;
1392 name = (char *) malloc( strlen( matPtr->name ) + 1 );
1393 strcpy( name, matPtr->name );
1394
1395#ifdef DEBUG
1396 if ( Tcl_GetCommandInfo( matPtr->interp, matPtr->name, &infoPtr ) )
1397 {
1398 if ( Tcl_DeleteCommand( matPtr->interp, matPtr->name ) == TCL_OK )
1399 fprintf( stderr, "Deleted command %s\n", name );
1400 else
1401 fprintf( stderr, "Unable to delete command %s\n", name );
1402 }
1403#else
1404 if ( Tcl_GetCommandInfo( matPtr->interp, matPtr->name, &infoPtr ) )
1405 Tcl_DeleteCommand( matPtr->interp, matPtr->name );
1406#endif
1407 free( (void *) name );
1408 }
1409 return (char *) NULL;
1410}
1411
1412//--------------------------------------------------------------------------
1413//
1414// DeleteMatrixCmd --
1415//
1416// Releases all the resources allocated to the matrix command.
1417// Invoked just before a matrix command is removed from an interpreter.
1418//
1419// Note: If the matrix has tracing enabled, it means the user
1420// explicitly deleted a non-persistent matrix. Not a good idea,
1421// because eventually the local variable that was being traced will
1422// become unset and the matrix data will be referenced in
1423// DeleteMatrixVar. So I've massaged this so that at worst it only
1424// causes a minor memory leak instead of imminent program death.
1425//
1426// Results:
1427// None.
1428//
1429// Side effects:
1430// All memory associated with the matrix operator is freed (usually).
1431//
1432//--------------------------------------------------------------------------
1433
1434static void
1435DeleteMatrixCmd( ClientData clientData )
1436{
1437 tclMatrix *matPtr = (tclMatrix *) clientData;
1438 Tcl_HashEntry *hPtr;
1439
1440 dbug_enter( "DeleteMatrixCmd" );
1441
1442#ifdef DEBUG
1443 fprintf( stderr, "Freeing space associated with matrix %s\n", matPtr->name );
1444#endif
1445
1446 // Remove hash table entry
1447
1448 hPtr = Tcl_FindHashEntry( &matTable, matPtr->name );
1449 if ( hPtr != NULL )
1450 Tcl_DeleteHashEntry( hPtr );
1451
1452 // Free data
1453
1454 if ( matPtr->fdata != NULL )
1455 {
1456 free( (void *) matPtr->fdata );
1457 matPtr->fdata = NULL;
1458 }
1459 if ( matPtr->idata != NULL )
1460 {
1461 free( (void *) matPtr->idata );
1462 matPtr->idata = NULL;
1463 }
1464 if ( matPtr->indices != NULL )
1465 {
1466 free( (void *) matPtr->indices );
1467 matPtr->indices = NULL;
1468 }
1469
1470 // Attempt to turn off tracing if possible.
1471
1472 if ( matPtr->tracing )
1473 {
1474 if ( Tcl_VarTraceInfo( matPtr->interp, matPtr->name, TCL_TRACE_UNSETS,
1475 (Tcl_VarTraceProc *) DeleteMatrixVar, NULL ) != NULL )
1476 {
1477 matPtr->tracing = 0;
1478 Tcl_UntraceVar( matPtr->interp, matPtr->name, TCL_TRACE_UNSETS,
1479 (Tcl_VarTraceProc *) DeleteMatrixVar, (ClientData) matPtr );
1480 Tcl_UnsetVar( matPtr->interp, matPtr->name, 0 );
1481 }
1482 }
1483
1484 // Free name.
1485
1486 if ( matPtr->name != NULL )
1487 {
1488 free( (void *) matPtr->name );
1489 matPtr->name = NULL;
1490 }
1491
1492 // Free tclMatrix
1493
1494 if ( !matPtr->tracing )
1495 free( (void *) matPtr );
1496#ifdef DEBUG
1497 else
1498 fprintf( stderr, "OOPS! You just lost %d bytes\n", sizeof ( tclMatrix ) );
1499#endif
1500}
#define min(x, y)
Definition nnpi.c:87
#define max(x, y)
Definition nnpi.c:88
static PLFLT value(double n1, double n2, double hue)
Definition plctrl.c:1219
#define PL_UNUSED(x)
Definition plplot.h:138
static int argc
Definition qt.cpp:48
static char ** argv
Definition qt.cpp:49
struct tclMatrixXtnsnDescr * next
Definition tclMatrix.h:363
tclMatrixXtnsnProc cmdproc
Definition tclMatrix.h:362
Mat_int * idata
Definition tclMatrix.h:77
int n[MAX_ARRAY_DIM]
Definition tclMatrix.h:71
char * name
Definition tclMatrix.h:74
int nindices
Definition tclMatrix.h:87
Mat_float * fdata
Definition tclMatrix.h:76
void(* put)(ClientData clientData, Tcl_Interp *interp, int index, const char *string)
Definition tclMatrix.h:83
void(* get)(ClientData clientData, Tcl_Interp *interp, int index, char *string)
Definition tclMatrix.h:84
Tcl_Interp * interp
Definition tclMatrix.h:79
int * indices
Definition tclMatrix.h:91
int tracing
Definition tclMatrix.h:72
int Tcl_Size
Definition tclAPI.c:53
char * strcpy(char *dst, const char *src)
int Tcl_MatrixCmd(ClientData PL_UNUSED(clientData), Tcl_Interp *interp, int argc, const char **argv)
Definition tclMatrix.c:128
tclMatrix * Tcl_GetMatrixPtr(Tcl_Interp *interp, const char *matName)
Definition tclMatrix.c:430
static int matTable_initted
Definition tclMatrix.c:70
#define MIN(a, b)
Definition tclMatrix.c:55
static void MatrixGet_f(ClientData clientData, Tcl_Interp *interp, int index, char *string)
Definition tclMatrix.c:1334
static void MatrixPut_f(ClientData clientData, Tcl_Interp *interp, int index, const char *string)
#define dbug_enter(a)
Definition tclMatrix.c:65
static int MatrixCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char **argv)
Definition tclMatrix.c:597
static void DeleteMatrixCmd(ClientData clientData)
Definition tclMatrix.c:1435
static void MatrixPut_i(ClientData clientData, Tcl_Interp *interp, int index, const char *string)
static Tcl_HashTable matTable
Definition tclMatrix.c:71
static char * DeleteMatrixVar(ClientData clientData, Tcl_Interp *interp, char *name1, char *name2, int flags)
int Tcl_MatrixInstallXtnsn(const char *cmd, tclMatrixXtnsnProc proc)
Definition tclMatrix.c:470
static int MatrixAssign(Tcl_Interp *interp, tclMatrix *m, int level, int *offset, int nargs, const char **args)
Definition tclMatrix.c:516
static void MatrixGet_i(ClientData clientData, Tcl_Interp *interp, int index, char *string)
static tclMatrixXtnsnDescr * tail
Definition tclMatrix.c:467
static tclMatrixXtnsnDescr * head
Definition tclMatrix.c:466
#define MAX(a, b)
Definition tclMatrix.c:52
@ TYPE_FLOAT
Definition tclMatrix.h:46
@ TYPE_INT
Definition tclMatrix.h:46
#define I3D(i, j, k)
Definition tclMatrix.h:56
int Mat_int
Definition tclMatrix.h:43
PLFLT Mat_float
Definition tclMatrix.h:38
int(* tclMatrixXtnsnProc)(tclMatrix *pm, Tcl_Interp *interp, int argc, const char *argv[])
Definition tclMatrix.h:356
#define MAX_ARRAY_DIM
Definition tclMatrix.h:52
static Tcl_Interp * interp
Definition tkMain.c:117
static const char * name
Definition tkMain.c:132