The Public Parts of MStack

All of the stack elements are allocated on the heap, so they will not exaust the program stack space. They may however exaust the heap if there are too many stack matrices allocated. The constructor, MStack(), is called first by the creation of Dispatch. The first call to the constructor sets level = 1. A static int records the fact that Dispatch has been created. All other calls to the constructor are through the Push() function.[*] Otherwise, the constructor initializes stackloc=0, level=0 and next = NULL. Push() fixes this. There is no destructor for MStack objects. They are deleted by pop.

Push() creates a MStack object on the heap, then inserts it into the stack. However the matrix stored in MStack is a reference to the base vector in the pushed matrix. It also increments Dispatch->stackloc and stores it in the new MStack object. It also stores Dispatch->level in the new object. Dispatch->Push() should be called before calling DecReturn() or ReturnMat().

Earlier versions of Push() had speed problems because Push() copied its argument into the new stack object. The current version uses a deep copy technique for storing stack matrices. The deep copy method boosted the speed of my programs by about 15 to 20 percent. Push() creates the reference by a call to the VMatrix function NewReference(). NewReference() increments the variable nrefs in the base vector. In the in-ram version, nrefs is incremented in the vdoub structure. In the virtual version, nrefs is incremented in the hdr structure. In either case, the base vector is not freed until nrefs is zero. However, the rest of the virtual matrix is deleted.

The stack matrices share the base vector with the pushed matrix, and hence you have indirect access to the stack matrix. This access is different from that in Eckle [#!EK:UC!#] where I found the deep-copy technique. Although I can't think of why you would want this access, here is how it can be accomplished.

        .
        .
        .
        VMatrix B = Tran(X)*X;
        Dispatch->Push(B);
        B.M(1,1) = 2.0;
        return Dispatch->DecReturn();
  }

Note since the stack top matrix shares the base vector with B, the assignment writes 2.0 in both B(1,1) and the (1,1) element of the stack top. Thus the matrix pushed onto the stack will be different from the matrix that is returned by the function call. The only place where this could be a problem is on the stack top and none of the other matrices in the stack. This is because the stack top is the matrix that is returned in function calls. I advise against using this trick. Again I say, Dispatch->Push() should only be called immediately before calling DecReturn() or ReturnMat().

Declevel() decrements the value of level. It is called by DecReturn(). It also stops the program if the nesting level has been decremented to zero. This error indicates that there is a mismatched pair of calls to Inclevel() and DecReturn(), or Inclevel() and Declevel().

Inclevel() increments the function nesting level. Usually Inclevel() is called whenever the function will use a matrix assignment, VMatrix::operator=(). DecReturn() calls Declevel(), so Inclevel() and DecReturn() should be used in pairs. Functions that use matrix assignment or matrix copy constructors do not always return matrices. In this case you should use a pair of Inclevel-Declevel calls to control the stack.

The code for DecReturn() and ReturnMat() is given below by

    VMatrix &DecReturn( void ){
         Declevel();
         next->level = level;
         return * ((VMatrix *) this->next);
     }
    VMatrix &ReturnMat( void ){
         next->level = level;
         return * ((VMatrix *) this->next);
     }
The two functions perform similar functions except that DecReturn() calls Declevel(). They both are called from Dispatch, so *this is actually Dispatch. The functions set level in next to Dispatch->level. You use ReturnMat() if you did not need matrix assignment in a function, and hence did not call Inclevel() at the top. You use DecReturn() if you needed to use matrix assignment hence called Inclevel() at the top. The return statment recasts Dispatch as a VMatrix pointer, and returns the matrix reference stored in next.

Cleanstack() is short:

  while( Dispatch->next->level >= Dispatch->level
         && Dispatch->next->next) Dispatch->Pop();
It calls Pop() while the next stack matrix has a level at least as large as Dispatch->level. It also stops the popping when there is only one matrix left on the stack which is useful when there are problems in the program. This clears the local stack. Recall that Cleanstack() is called by VMatrix::operator=(). The equals operator replaces its left operand by the right before calling Cleanstack(). The left operand is not in the stack, so it is not deleted by Pop().

The copy constructor calls Cleanstack() so it also eliminates temporary matrices. Therefore, you should call Inclevel() and Declevel() if you intend to use the copy constructors.

You may wish to call Cleanstack() occasionally if you are in the habit of using statements such as (Tran(X)*X).DispalayMat();. [*] These statement–function–calls leave matrices on the stack which are removed automatically during a matrix assignment, a copy constructor, or by an explicit call to Cleanstack().

PrintStack() traverses the stack. It reports the stackloc, level, r, c and name for each matrix in the stack.