source: branches/opt_cond_exp/cppad/local/forward0sweep.hpp @ 2978

Last change on this file since 2978 was 2978, checked in by bradbell, 7 years ago

Change conditional skip to skipping operations instead of skipping variables.

  • Property svn:keywords set to Id
File size: 19.0 KB
Line 
1/* $Id: forward0sweep.hpp 2978 2013-10-20 15:38:57Z bradbell $ */
2# ifndef CPPAD_FORWARD0SWEEP_INCLUDED
3# define CPPAD_FORWARD0SWEEP_INCLUDED
4
5/* --------------------------------------------------------------------------
6CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-13 Bradley M. Bell
7
8CppAD is distributed under multiple licenses. This distribution is under
9the terms of the
10                    Eclipse Public License Version 1.0.
11
12A copy of this license is included in the COPYING file of this distribution.
13Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
14-------------------------------------------------------------------------- */
15
16namespace CppAD { // BEGIN_CPPAD_NAMESPACE
17/*!
18\defgroup forward0sweep_hpp forward0sweep.hpp
19\{
20\file forward0sweep.hpp
21Compute zero order forward mode Taylor coefficients.
22*/
23
24/*
25\def CPPAD_ATOMIC_CALL
26This avoids warnings when NDEBUG is defined and user_ok is not used.
27If \c NDEBUG is defined, this resolves to
28\code
29        user_atom->forward
30\endcode
31otherwise, it respolves to
32\code
33        user_ok = user_atom->forward
34\endcode
35This maco is undefined at the end of this file to facillitate is
36use with a different definition in other files.
37*/
38# ifdef NDEBUG
39# define CPPAD_ATOMIC_CALL user_atom->forward
40# else
41# define CPPAD_ATOMIC_CALL user_ok = user_atom->forward
42# endif
43
44/*!
45\def CPPAD_FORWARD0SWEEP_TRACE
46This value is either zero or one.
47Zero is the normal operational value.
48If it is one, a trace of every forward0sweep computation is printed.
49(Note that forward0sweep is not used if CPPAD_USE_FORWARD0SWEEP is zero).
50*/
51# define CPPAD_FORWARD0SWEEP_TRACE 0
52
53/*!
54Compute zero order forward mode Taylor coefficients.
55
56\tparam Base
57The type used during the forward mode computations; i.e., the corresponding
58recording of operations used the type \c AD<Base>.
59
60\param s_out
61Is the stream where output corresponding to \c PriOp operations will
62be written.
63
64\param print
65If \a print is false,
66suppress the output that is otherwise generated by the c PriOp instructions.
67
68\param n
69is the number of independent variables on the tape.
70
71\param numvar
72is the total number of variables on the tape.
73This is also equal to the number of rows in the matrix \a Taylor; i.e.,
74\a Rec->num_rec_var().
75
76\param Rec
77The information stored in \a Rec
78is a recording of the operations corresponding to the function
79\f[
80        F : {\bf R}^n \rightarrow {\bf R}^m
81\f]
82where \f$ n \f$ is the number of independent variables and
83\f$ m \f$ is the number of dependent variables.
84\n
85\n
86The object \a Rec is effectly constant.
87There are two exceptions to this.
88The first exception is that while palying back the tape
89the object \a Rec holds information about the current location
90with in the tape and this changes during palyback.
91The second exception is the fact that the
92zero order ( \a d = 0 ) versions of the VecAD operators LdpOp and LdvOp
93modify the corresponding \a op_arg values returned by
94\ref player::next_forward and \ref player::next_reverse; see the
95\link load_op.hpp LdpOp and LdvOp \endlink operations.
96
97\param J
98Is the number of columns in the coefficient matrix \a Taylor.
99This must be greater than or equal one.
100
101\param Taylor
102\b Input: For j = 1 , ... , \a n,
103\a Taylor [ j * J + 0 ]
104variable with index i on the tape
105(independent variable with index (j-1) in the independent variable vector).
106\n
107\n
108\b Output: For i = \a n + 1, ... , \a numvar - 1,
109\a Taylor [ i * J + 0 ]
110is the zero order Taylor coefficient for the variable with
111index i on the tape.
112
113\param cskip_op
114Is a vector with size Rec->num_rec_op(),
115the input value of the elements does not matter.
116Upon return, if cskip_op[i] is true, the operator index i in the recording
117does not affect any of the dependent variable (given the value
118of the independent variables).
119
120\a return
121The return value is equal to the number of ComOp operations
122that have a different result from when the information in
123\a Rec was recorded.
124(Note that if NDEBUG is true, there are no ComOp operations
125in Rec and hence this return value is always zero.)
126*/
127
128template <class Base>
129size_t forward0sweep(
130        std::ostream&         s_out,
131        bool                  print,
132        size_t                n,
133        size_t                numvar,
134        player<Base>         *Rec,
135        size_t                J,
136        Base                 *Taylor,
137        CppAD::vector<bool>&  cskip_op
138)
139{       CPPAD_ASSERT_UNKNOWN( J >= 1 );
140
141        // op code for current instruction
142        OpCode           op;
143
144        // index for current instruction
145        size_t         i_op;
146
147        // next variables
148        size_t        i_var;
149
150        // constant and non-constant version of the operation argument indices
151        addr_t*         non_const_arg;
152        const addr_t*   arg = CPPAD_NULL;
153
154        // initialize the comparision operator (ComOp) counter
155        size_t compareCount = 0;
156
157        // This is an order zero calculation, initialize vector indices
158        pod_vector<size_t> VectorInd;  // address for each element
159        pod_vector<bool>   VectorVar;  // is element a variable
160        size_t  i = Rec->num_rec_vecad_ind();
161        if( i > 0 )
162        {       VectorInd.extend(i);
163                VectorVar.extend(i);
164                while(i--)
165                {       VectorInd[i] = Rec->GetVecInd(i);
166                        VectorVar[i] = false;
167                }
168        }
169
170        // zero order, so initialize conditional skip flags
171        for(i = 0; i < Rec->num_rec_op(); i++)
172                cskip_op[i] = false;
173
174        // work space used by UserOp.
175        const size_t user_q = 0;     // lowest order
176        const size_t user_p = 0;     // highest order
177        vector<bool> user_vx;        // empty vecotor
178        vector<bool> user_vy;        // empty vecotor
179        vector<Base> user_tx;        // argument vector Taylor coefficients
180        vector<Base> user_ty;        // result vector Taylor coefficients
181        size_t user_index = 0;       // indentifier for this atomic operation
182        size_t user_id    = 0;       // user identifier for this call to operator
183        size_t user_i     = 0;       // index in result vector
184        size_t user_j     = 0;       // index in argument vector
185        size_t user_m     = 0;       // size of result vector
186        size_t user_n     = 0;       // size of arugment vector
187        //
188        atomic_base<Base>* user_atom = CPPAD_NULL; // user's atomic op calculator
189# ifndef NDEBUG
190        bool               user_ok   = false;      // atomic op return value
191# endif
192        //
193        // next expected operator in a UserOp sequence
194        enum { user_start, user_arg, user_ret, user_end } user_state = user_start;
195
196        // check numvar argument
197        CPPAD_ASSERT_UNKNOWN( Rec->num_rec_var() == numvar );
198
199        // length of the parameter vector (used by CppAD assert macros)
200        const size_t num_par = Rec->num_rec_par();
201
202        // length of the text vector (used by CppAD assert macros)
203        const size_t num_text = Rec->num_rec_text();
204
205        // pointer to the beginning of the parameter vector
206        const Base* parameter = CPPAD_NULL;
207        if( num_par > 0 )
208                parameter = Rec->GetPar();
209
210        // pointer to the beginning of the text vector
211        const char* text = CPPAD_NULL;
212        if( num_text > 0 )
213                text = Rec->GetTxt(0);
214
215        // skip the BeginOp at the beginning of the recording
216        Rec->start_forward(op, arg, i_op, i_var);
217        CPPAD_ASSERT_UNKNOWN( op == BeginOp );
218# if CPPAD_FORWARD0SWEEP_TRACE
219        std::cout << std::endl;
220# endif
221        while(op != EndOp)
222        {
223                // this op
224                Rec->next_forward(op, arg, i_op, i_var);
225                CPPAD_ASSERT_UNKNOWN( (i_op > n)  | (op == InvOp) ); 
226                CPPAD_ASSERT_UNKNOWN( (i_op <= n) | (op != InvOp) ); 
227
228                // check if we are skipping this operation
229                while( cskip_op[i_op] )
230                {       if( op == CSumOp )
231                        {       // CSumOp has a variable number of arguments and
232                                Rec->forward_csum(op, arg, i_op, i_var);
233                        }
234                        Rec->next_forward(op, arg, i_op, i_var);
235                }
236
237                // action to take depends on the case
238                switch( op )
239                {
240                        case AbsOp:
241                        forward_abs_op_0(i_var, arg[0], J, Taylor);
242                        break;
243                        // -------------------------------------------------
244
245                        case AddvvOp:
246                        forward_addvv_op_0(i_var, arg, parameter, J, Taylor);
247                        break;
248                        // -------------------------------------------------
249
250                        case AddpvOp:
251                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
252                        forward_addpv_op_0(i_var, arg, parameter, J, Taylor);
253                        break;
254                        // -------------------------------------------------
255
256                        case AcosOp:
257                        // sqrt(1 - x * x), acos(x)
258                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
259                        forward_acos_op_0(i_var, arg[0], J, Taylor);
260                        break;
261                        // -------------------------------------------------
262
263                        case AsinOp:
264                        // sqrt(1 - x * x), asin(x)
265                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
266                        forward_asin_op_0(i_var, arg[0], J, Taylor);
267                        break;
268                        // -------------------------------------------------
269
270                        case AtanOp:
271                        // 1 + x * x, atan(x)
272                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
273                        forward_atan_op_0(i_var, arg[0], J, Taylor);
274                        break;
275                        // -------------------------------------------------
276
277                        case CExpOp:
278                        // Use the general case with d == 0
279                        // (could create an optimzied verison for this case)
280                        forward_cond_op_0(
281                                i_var, arg, num_par, parameter, J, Taylor
282                        );
283                        break;
284                        // ---------------------------------------------------
285                        case ComOp:
286                        forward_comp_op_0(
287                        compareCount, arg, num_par, parameter, J, Taylor
288                        );
289                        break;
290                        // ---------------------------------------------------
291
292                        case CosOp:
293                        // sin(x), cos(x)
294                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
295                        forward_cos_op_0(i_var, arg[0], J, Taylor);
296                        break;
297                        // ---------------------------------------------------
298
299                        case CoshOp:
300                        // sinh(x), cosh(x)
301                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
302                        forward_cosh_op_0(i_var, arg[0], J, Taylor);
303                        break;
304                        // -------------------------------------------------
305
306                        case CSkipOp:
307                        // CSumOp has a variable number of arguments and
308                        // next_forward thinks it one has one argument.
309                        // we must inform next_forward of this special case.
310                        Rec->forward_cskip(op, arg, i_op, i_var);
311                        forward_cskip_op_0(
312                                i_var, arg, num_par, parameter, J, Taylor, cskip_op
313                        );
314                        break;
315                        // -------------------------------------------------
316
317                        case CSumOp:
318                        // CSumOp has a variable number of arguments and
319                        // next_forward thinks it one has one argument.
320                        // we must inform next_forward of this special case.
321                        Rec->forward_csum(op, arg, i_op, i_var);
322                        forward_csum_op(
323                                0, 0, i_var, arg, num_par, parameter, J, Taylor
324                        );
325                        break;
326                        // -------------------------------------------------
327
328                        case DisOp:
329                        forward_dis_op_0(i_var, arg, J, Taylor);
330                        break;
331                        // -------------------------------------------------
332
333                        case DivvvOp:
334                        forward_divvv_op_0(i_var, arg, parameter, J, Taylor);
335                        break;
336                        // -------------------------------------------------
337
338                        case DivpvOp:
339                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
340                        forward_divpv_op_0(i_var, arg, parameter, J, Taylor);
341                        break;
342                        // -------------------------------------------------
343
344                        case DivvpOp:
345                        CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par );
346                        forward_divvp_op_0(i_var, arg, parameter, J, Taylor);
347                        break;
348                        // -------------------------------------------------
349
350                        case EndOp:
351                        CPPAD_ASSERT_NARG_NRES(op, 0, 0);
352                        break;
353                        // -------------------------------------------------
354
355                        case ExpOp:
356                        forward_exp_op_0(i_var, arg[0], J, Taylor);
357                        break;
358                        // -------------------------------------------------
359
360                        case InvOp:
361                        break;
362                        // -------------------------------------------------
363
364                        case LdpOp:
365                        CPPAD_ASSERT_UNKNOWN( VectorInd.size() != 0 );
366                        CPPAD_ASSERT_UNKNOWN( VectorVar.size() != 0 );
367                        non_const_arg = Rec->forward_non_const_arg();
368                        forward_load_p_op_0(
369                                i_var, 
370                                non_const_arg, 
371                                num_par, 
372                                parameter, 
373                                J, 
374                                Taylor,
375                                Rec->num_rec_vecad_ind(),
376                                VectorVar.data(),
377                                VectorInd.data()
378                        );
379                        break;
380                        // -------------------------------------------------
381
382                        case LdvOp:
383                        CPPAD_ASSERT_UNKNOWN( VectorInd.size() != 0 );
384                        CPPAD_ASSERT_UNKNOWN( VectorVar.size() != 0 );
385                        non_const_arg = Rec->forward_non_const_arg();
386                        forward_load_v_op_0(
387                                i_var, 
388                                non_const_arg, 
389                                num_par, 
390                                parameter, 
391                                J, 
392                                Taylor,
393                                Rec->num_rec_vecad_ind(),
394                                VectorVar.data(),
395                                VectorInd.data()
396                        );
397                        break;
398                        // -------------------------------------------------
399
400                        case LogOp:
401                        forward_log_op_0(i_var, arg[0], J, Taylor);
402                        break;
403                        // -------------------------------------------------
404
405                        case MulvvOp:
406                        forward_mulvv_op_0(i_var, arg, parameter, J, Taylor);
407                        break;
408                        // -------------------------------------------------
409
410                        case MulpvOp:
411                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
412                        forward_mulpv_op_0(i_var, arg, parameter, J, Taylor);
413                        break;
414                        // -------------------------------------------------
415
416                        case ParOp:
417                        forward_par_op_0(
418                                i_var, arg, num_par, parameter, J, Taylor
419                        );
420                        break;
421                        // -------------------------------------------------
422
423                        case PowvpOp:
424                        CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par );
425                        forward_powvp_op_0(i_var, arg, parameter, J, Taylor);
426                        break;
427                        // -------------------------------------------------
428
429                        case PowpvOp:
430                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
431                        forward_powpv_op_0(i_var, arg, parameter, J, Taylor);
432                        break;
433                        // -------------------------------------------------
434
435                        case PowvvOp:
436                        forward_powvv_op_0(i_var, arg, parameter, J, Taylor);
437                        break;
438                        // -------------------------------------------------
439
440                        case PriOp:
441                        if( print ) forward_pri_0(s_out,
442                                i_var, arg, num_text, text, num_par, parameter, J, Taylor
443                        );
444                        break;
445                        // -------------------------------------------------
446
447                        case SignOp:
448                        // cos(x), sin(x)
449                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
450                        forward_sign_op_0(i_var, arg[0], J, Taylor);
451                        break;
452                        // -------------------------------------------------
453
454                        case SinOp:
455                        // cos(x), sin(x)
456                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
457                        forward_sin_op_0(i_var, arg[0], J, Taylor);
458                        break;
459                        // -------------------------------------------------
460
461                        case SinhOp:
462                        // cosh(x), sinh(x)
463                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
464                        forward_sinh_op_0(i_var, arg[0], J, Taylor);
465                        break;
466                        // -------------------------------------------------
467
468                        case SqrtOp:
469                        forward_sqrt_op_0(i_var, arg[0], J, Taylor);
470                        break;
471                        // -------------------------------------------------
472
473                        case StppOp:
474                        forward_store_pp_op_0(
475                                i_var, 
476                                arg, 
477                                num_par, 
478                                J, 
479                                Taylor,
480                                Rec->num_rec_vecad_ind(),
481                                VectorVar.data(),
482                                VectorInd.data()
483                        );
484                        break;
485                        // -------------------------------------------------
486
487                        case StpvOp:
488                        forward_store_pv_op_0(
489                                i_var, 
490                                arg, 
491                                num_par, 
492                                J, 
493                                Taylor,
494                                Rec->num_rec_vecad_ind(),
495                                VectorVar.data(),
496                                VectorInd.data()
497                        );
498                        break;
499                        // -------------------------------------------------
500
501                        case StvpOp:
502                        forward_store_vp_op_0(
503                                i_var, 
504                                arg, 
505                                num_par, 
506                                J, 
507                                Taylor,
508                                Rec->num_rec_vecad_ind(),
509                                VectorVar.data(),
510                                VectorInd.data()
511                        );
512                        break;
513                        // -------------------------------------------------
514
515                        case StvvOp:
516                        forward_store_vv_op_0(
517                                i_var, 
518                                arg, 
519                                num_par, 
520                                J, 
521                                Taylor,
522                                Rec->num_rec_vecad_ind(),
523                                VectorVar.data(),
524                                VectorInd.data()
525                        );
526                        break;
527                        // -------------------------------------------------
528
529                        case SubvvOp:
530                        forward_subvv_op_0(i_var, arg, parameter, J, Taylor);
531                        break;
532                        // -------------------------------------------------
533
534                        case SubpvOp:
535                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
536                        forward_subpv_op_0(i_var, arg, parameter, J, Taylor);
537                        break;
538                        // -------------------------------------------------
539
540                        case SubvpOp:
541                        CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par );
542                        forward_subvp_op_0(i_var, arg, parameter, J, Taylor);
543                        break;
544                        // -------------------------------------------------
545
546                        case TanOp:
547                        // tan(x)^2, tan(x)
548                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
549                        forward_tan_op_0(i_var, arg[0], J, Taylor);
550                        break;
551                        // -------------------------------------------------
552
553                        case TanhOp:
554                        // tanh(x)^2, tanh(x)
555                        CPPAD_ASSERT_UNKNOWN( i_var < numvar  );
556                        forward_tanh_op_0(i_var, arg[0], J, Taylor);
557                        break;
558                        // -------------------------------------------------
559
560                        case UserOp:
561                        // start or end an atomic operation sequence
562                        CPPAD_ASSERT_UNKNOWN( NumRes( UserOp ) == 0 );
563                        CPPAD_ASSERT_UNKNOWN( NumArg( UserOp ) == 4 );
564                        if( user_state == user_start )
565                        {       user_index = arg[0];
566                                user_id    = arg[1];
567                                user_n     = arg[2];
568                                user_m     = arg[3];
569                                user_atom  = atomic_base<Base>::class_object(user_index);
570# ifndef NDEBUG
571                                if( user_atom == CPPAD_NULL )
572                                {       std::string msg = 
573                                                atomic_base<Base>::class_name(user_index)
574                                                + ": atomic_base function has been deleted";
575                                        CPPAD_ASSERT_KNOWN(false, msg.c_str() );
576                                }
577# endif
578                                if(user_tx.size() != user_n)
579                                        user_tx.resize(user_n);
580                                if(user_ty.size() != user_m)
581                                        user_ty.resize(user_m);
582                                user_j     = 0;
583                                user_i     = 0;
584                                user_state = user_arg;
585                        }
586                        else
587                        {       CPPAD_ASSERT_UNKNOWN( user_state == user_end );
588                                CPPAD_ASSERT_UNKNOWN( user_index == size_t(arg[0]) );
589                                CPPAD_ASSERT_UNKNOWN( user_id    == size_t(arg[1]) );
590                                CPPAD_ASSERT_UNKNOWN( user_n     == size_t(arg[2]) );
591                                CPPAD_ASSERT_UNKNOWN( user_m     == size_t(arg[3]) );
592# ifndef NDEBUG
593                                if( ! user_ok )
594                                {       std::string msg = 
595                                                atomic_base<Base>::class_name(user_index)
596                                                + ": atomic_base.forward: returned false";
597                                        CPPAD_ASSERT_KNOWN(false, msg.c_str() );
598                                }
599# endif
600                                user_state = user_start;
601                        }
602                        break;
603
604                        case UsrapOp:
605                        // parameter argument in an atomic operation sequence
606                        CPPAD_ASSERT_UNKNOWN( user_state == user_arg );
607                        CPPAD_ASSERT_UNKNOWN( user_j < user_n );
608                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
609                        user_tx[user_j++] = parameter[ arg[0] ];
610                        if( user_j == user_n )
611                        {       // call users function for this operation
612                                user_atom->set_id(user_id);
613                                CPPAD_ATOMIC_CALL(user_q, user_p, 
614                                        user_vx, user_vy, user_tx, user_ty
615                                );
616                                user_state = user_ret;
617                        }
618                        break;
619
620                        case UsravOp:
621                        // variable argument in an atomic operation sequence
622                        CPPAD_ASSERT_UNKNOWN( user_state == user_arg );
623                        CPPAD_ASSERT_UNKNOWN( user_j < user_n );
624                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) <= i_var );
625                        user_tx[user_j++] = Taylor[ arg[0] * J + 0 ];
626                        if( user_j == user_n )
627                        {       // call users function for this operation
628                                user_atom->set_id(user_id);
629                                CPPAD_ATOMIC_CALL(user_q, user_p, 
630                                        user_vx, user_vy, user_tx, user_ty
631                                );
632                                user_state = user_ret;
633                        }
634                        break;
635
636                        case UsrrpOp:
637                        // parameter result in an atomic operation sequence
638                        CPPAD_ASSERT_UNKNOWN( user_state == user_ret );
639                        CPPAD_ASSERT_UNKNOWN( user_i < user_m );
640                        user_i++;
641                        if( user_i == user_m )
642                                user_state = user_end;
643                        break;
644
645                        case UsrrvOp:
646                        // variable result in an atomic operation sequence
647                        CPPAD_ASSERT_UNKNOWN( user_state == user_ret );
648                        CPPAD_ASSERT_UNKNOWN( user_i < user_m );
649                        Taylor[ i_var * J + 0 ] = user_ty[user_i++];
650                        if( user_i == user_m )
651                                user_state = user_end;
652                        break;
653                        // -------------------------------------------------
654
655                        default:
656                        CPPAD_ASSERT_UNKNOWN(false);
657                }
658# if CPPAD_FORWARD0SWEEP_TRACE
659                size_t       d      = 0;
660                size_t       i_tmp  = i_var;
661                Base*        Z_tmp  = Taylor + i_var * J;
662
663                printOp(
664                        std::cout, 
665                        Rec,
666                        i_tmp,
667                        op, 
668                        arg,
669                        d + 1, 
670                        Z_tmp, 
671                        0, 
672                        (Base *) CPPAD_NULL
673                );
674        }
675        std::cout << std::endl;
676# else
677        }
678# endif
679        CPPAD_ASSERT_UNKNOWN( user_state == user_start );
680        CPPAD_ASSERT_UNKNOWN( i_var + 1 == Rec->num_rec_var() );
681
682        return compareCount;
683}
684
685/*! \} */
686} // END_CPPAD_NAMESPACE
687
688// preprocessor symbols that are local to this file
689# undef CPPAD_FORWARD0SWEEP_TRACE
690# undef CPPAD_ATOMIC_CALL
691
692# endif
Note: See TracBrowser for help on using the repository browser.