1/* ------------------------------------------------------------------
2 * Copyright (C) 1998-2009 PacketVideo
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
13 * express or implied.
14 * See the License for the specific language governing permissions
15 * and limitations under the License.
16 * -------------------------------------------------------------------
17 */
18/****************************************************************************************
19Portions of this file are derived from the following 3GPP standard:
20
21    3GPP TS 26.073
22    ANSI-C code for the Adaptive Multi-Rate (AMR) speech codec
23    Available from http://www.3gpp.org
24
25(C) 2004, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
26Permission to distribute, modify and use this file under the standard license
27terms listed above has been obtained from the copyright holder.
28****************************************************************************************/
29/*
30------------------------------------------------------------------------------
31
32
33
34 Pathname: ./audio/gsm-amr/c/src/c3_14pf.c
35 Functions:
36
37     Date: 05/26/2000
38
39------------------------------------------------------------------------------
40 REVISION HISTORY
41
42 Description: Modified to pass overflow flag through to basic math function.
43 The flag is passed back to the calling function by pointer reference.
44
45 Description: Optimized file to reduce clock cycle usage. Updated copyright
46              year. Removed unneccesary include files and added only the
47              include files for the math functions used. Removed unused
48              #defines.
49
50 Description: Changed round function name to pv_round to avoid conflict with
51              round function in C standard library.
52
53 Description:  Replaced "int" and/or "char" with OSCL defined types.
54
55 Description:
56
57------------------------------------------------------------------------------
58 MODULE DESCRIPTION
59
60------------------------------------------------------------------------------
61*/
62
63/*----------------------------------------------------------------------------
64; INCLUDES
65----------------------------------------------------------------------------*/
66#include "c3_14pf.h"
67#include "typedef.h"
68#include "inv_sqrt.h"
69#include "cnst.h"
70#include "cor_h.h"
71#include "set_sign.h"
72#include "basic_op.h"
73
74/*----------------------------------------------------------------------------
75; MACROS
76; Define module specific macros here
77----------------------------------------------------------------------------*/
78
79/*----------------------------------------------------------------------------
80; DEFINES
81; Include all pre-processor statements here. Include conditional
82; compile variables also.
83----------------------------------------------------------------------------*/
84
85#define NB_PULSE  3
86
87/*----------------------------------------------------------------------------
88; LOCAL FUNCTION DEFINITIONS
89; Function Prototype declaration
90----------------------------------------------------------------------------*/
91static void search_3i40(
92    Word16 dn[],        /* i : correlation between target and h[]            */
93    Word16 dn2[],       /* i : maximum of corr. in each track.               */
94    Word16 rr[][L_CODE],/* i : matrix of autocorrelation                     */
95    Word16 codvec[],    /* o : algebraic codebook vector                     */
96    Flag   * pOverflow  /* o : Flag set when overflow occurs                 */
97);
98
99static Word16 build_code(
100    Word16 codvec[],    /* i : algebraic codebook vector                     */
101    Word16 dn_sign[],   /* i : sign of dn[]                                  */
102    Word16 cod[],       /* o : algebraic (fixed) codebook excitation         */
103    Word16 h[],         /* i : impulse response of weighted synthesis filter */
104    Word16 y[],         /* o : filtered fixed codebook excitation            */
105    Word16 sign[],      /* o : sign of 3 pulses                              */
106    Flag   * pOverflow  /* o : Flag set when overflow occurs                 */
107);
108
109/*----------------------------------------------------------------------------
110; LOCAL VARIABLE DEFINITIONS
111; Variable declaration - defined here and used outside this module
112----------------------------------------------------------------------------*/
113
114/*----------------------------------------------------------------------------
115; EXTERNAL GLOBAL STORE/BUFFER/POINTER REFERENCES
116; Declare variables used in this module but defined elsewhere
117----------------------------------------------------------------------------*/
118
119/*
120------------------------------------------------------------------------------
121 FUNCTION NAME: code_3i40_14bits
122------------------------------------------------------------------------------
123 INPUT AND OUTPUT DEFINITIONS
124
125 Inputs:
126    x[]   Array of type Word16 -- target vector
127    h[]   Array of type Word16 -- impulse response of weighted synthesis filter
128                                  h[-L_subfr..-1] must be set to zero.
129
130    T0           Array of type Word16 -- Pitch lag
131    pitch_sharp, Array of type Word16 --  Last quantized pitch gain
132
133 Outputs:
134    code[]  Array of type Word16 -- Innovative codebook
135    y[]     Array of type Word16 -- filtered fixed codebook excitation
136    * sign  Pointer of type Word16 -- Pointer to the signs of 3 pulses
137    pOverflow    Pointer to Flag      -- set when overflow occurs
138
139 Returns:
140    index
141
142 Global Variables Used:
143    None
144
145 Local Variables Needed:
146    None
147
148------------------------------------------------------------------------------
149 FUNCTION DESCRIPTION
150
151 PURPOSE:  Searches a 14 bit algebraic codebook containing 3 pulses
152           in a frame of 40 samples.
153
154 DESCRIPTION:
155    The code length is 40, containing 3 nonzero pulses: i0...i2.
156    All pulses can have two possible amplitudes: +1 or -1.
157    Pulse i0 can have 8 possible positions, pulses i1 and i2 can have
158    2x8=16 positions.
159
160        i0 :  0, 5, 10, 15, 20, 25, 30, 35.
161        i1 :  1, 6, 11, 16, 21, 26, 31, 36.
162              3, 8, 13, 18, 23, 28, 33, 38.
163        i2 :  2, 7, 12, 17, 22, 27, 32, 37.
164              4, 9, 14, 19, 24, 29, 34, 39.
165
166------------------------------------------------------------------------------
167 REQUIREMENTS
168
169 None
170
171------------------------------------------------------------------------------
172 REFERENCES
173
174 [1] c3_14pf.c, UMTS GSM AMR speech codec, R99 - Version 3.2.0, March 2, 2001
175
176------------------------------------------------------------------------------
177 PSEUDO-CODE
178
179------------------------------------------------------------------------------
180 RESOURCES USED [optional]
181
182 When the code is written for a specific target processor the
183 the resources used should be documented below.
184
185 HEAP MEMORY USED: x bytes
186
187 STACK MEMORY USED: x bytes
188
189 CLOCK CYCLES: (cycle count equation for this function) + (variable
190                used to represent cycle count for each subroutine
191                called)
192     where: (cycle count variable) = cycle count for [subroutine
193                                     name]
194
195------------------------------------------------------------------------------
196 CAUTION [optional]
197 [State any special notes, constraints or cautions for users of this function]
198
199------------------------------------------------------------------------------
200*/
201
202Word16 code_3i40_14bits(
203    Word16 x[],         /* i : target vector                                 */
204    Word16 h[],         /* i : impulse response of weighted synthesis filter */
205    /*     h[-L_subfr..-1] must be set to zero.          */
206    Word16 T0,          /* i : Pitch lag                                     */
207    Word16 pitch_sharp, /* i : Last quantized pitch gain                     */
208    Word16 code[],      /* o : Innovative codebook                           */
209    Word16 y[],         /* o : filtered fixed codebook excitation            */
210    Word16 * sign,      /* o : Signs of 3 pulses                             */
211    Flag   * pOverflow  /* o : Flag set when overflow occurs                 */
212)
213{
214    Word16 codvec[NB_PULSE];
215    Word16 dn[L_CODE];
216    Word16 dn2[L_CODE];
217    Word16 dn_sign[L_CODE];
218    Word16 rr[L_CODE][L_CODE];
219    Word16 i;
220    Word16 index;
221    Word16 sharp;
222    Word16 tempWord;
223
224    /* sharp = shl(pitch_sharp, 1, pOverflow); */
225    sharp = pitch_sharp << 1;
226
227    if (T0 < L_CODE)
228    {
229        for (i = T0; i < L_CODE; i++)
230        {
231            tempWord =
232                mult(
233                    h[i - T0],
234                    sharp,
235                    pOverflow);
236
237            h[i] =
238                add(
239                    h[i],
240                    tempWord,
241                    pOverflow);
242        }
243    }
244
245    cor_h_x(
246        h,
247        x,
248        dn,
249        1,
250        pOverflow);
251
252    set_sign(
253        dn,
254        dn_sign,
255        dn2,
256        6);
257
258    cor_h(
259        h,
260        dn_sign,
261        rr,
262        pOverflow);
263
264    search_3i40(
265        dn,
266        dn2,
267        rr,
268        codvec,
269        pOverflow);
270
271    /* function result */
272    index =
273        build_code(
274            codvec,
275            dn_sign,
276            code,
277            h,
278            y,
279            sign,
280            pOverflow);
281
282    /*-----------------------------------------------------------------*
283    * Compute innovation vector gain.                                 *
284    * Include fixed-gain pitch contribution into code[].              *
285    *-----------------------------------------------------------------*/
286
287    if (T0 < L_CODE)
288    {
289        for (i = T0; i < L_CODE; i++)
290        {
291            tempWord =
292                mult(
293                    code[i - T0],
294                    sharp,
295                    pOverflow);
296
297            code[i] =
298                add(
299                    code[i],
300                    tempWord,
301                    pOverflow);
302        }
303    }
304    return index;
305}
306
307/****************************************************************************/
308
309/*
310------------------------------------------------------------------------------
311 FUNCTION NAME: search_3i40
312------------------------------------------------------------------------------
313 INPUT AND OUTPUT DEFINITIONS
314
315 Inputs:
316    dn[]         Array of type Word16 -- correlation between target and h[]
317    dn2[]        Array of type Word16 -- maximum of corr. in each track.
318    rr[][L_CODE] Double Array of type Word16 -- autocorrelation matrix
319
320 Outputs:
321    codvec[]     Array of type Word16 -- algebraic codebook vector
322    pOverflow    Pointer to Flag      -- set when overflow occurs
323
324 Returns:
325    None
326
327 Global Variables Used:
328    None
329
330 Local Variables Needed:
331    None
332
333------------------------------------------------------------------------------
334 FUNCTION DESCRIPTION
335
336 PURPOSE: Search the best codevector; determine positions of the 3 pulses
337          in the 40-sample frame.
338------------------------------------------------------------------------------
339 REQUIREMENTS
340
341 None
342
343------------------------------------------------------------------------------
344 REFERENCES
345
346 [1] c3_14pf.c, UMTS GSM AMR speech codec, R99 - Version 3.2.0, March 2, 2001
347
348------------------------------------------------------------------------------
349 PSEUDO-CODE
350
351------------------------------------------------------------------------------
352 RESOURCES USED [optional]
353
354 When the code is written for a specific target processor the
355 the resources used should be documented below.
356
357 HEAP MEMORY USED: x bytes
358
359 STACK MEMORY USED: x bytes
360
361 CLOCK CYCLES: (cycle count equation for this function) + (variable
362                used to represent cycle count for each subroutine
363                called)
364     where: (cycle count variable) = cycle count for [subroutine
365                                     name]
366
367------------------------------------------------------------------------------
368 CAUTION [optional]
369 [State any special notes, constraints or cautions for users of this function]
370
371------------------------------------------------------------------------------
372*/
373static void search_3i40(
374    Word16 dn[],         /* i : correlation between target and h[] */
375    Word16 dn2[],        /* i : maximum of corr. in each track.    */
376    Word16 rr[][L_CODE], /* i : matrix of autocorrelation          */
377    Word16 codvec[],     /* o : algebraic codebook vector          */
378    Flag   * pOverflow   /* o : Flag set when overflow occurs      */
379)
380{
381    Word16 i0;
382    Word16 i1;
383    Word16 i2;
384
385    Word16 ix = 0; /* initialization only needed to keep gcc silent */
386    Word16 ps = 0; /* initialization only needed to keep gcc silent */
387
388    Word16 i;
389    Word16 pos;
390    Word16 track1;
391    Word16 track2;
392    Word16 ipos[NB_PULSE];
393
394    Word16 psk;
395    Word16 ps0;
396    Word16 ps1;
397    Word16 sq;
398    Word16 sq1;
399    Word16 alpk;
400    Word16 alp;
401    Word16 alp_16;
402
403    Word16 *p_codvec = &codvec[0];
404
405    Word32 s;
406    Word32 alp0;
407    Word32 alp1;
408
409    psk = -1;
410    alpk = 1;
411
412    for (i = 0; i < NB_PULSE; i++)
413    {
414        *(p_codvec++) = i;
415    }
416
417    for (track1 = 1; track1 < 4; track1 += 2)
418    {
419        for (track2 = 2; track2 < 5; track2 += 2)
420        {
421            /* fix starting position */
422
423            ipos[0] = 0;
424            ipos[1] = track1;
425            ipos[2] = track2;
426
427            /*------------------------------------------------------------------*
428             * main loop: try 3 tracks.                                         *
429             *------------------------------------------------------------------*/
430
431            for (i = 0; i < NB_PULSE; i++)
432            {
433                /*----------------------------------------------------------------*
434                 * i0 loop: try 8 positions.                                      *
435                 *----------------------------------------------------------------*/
436
437                /* account for ptr. init. (rr[io]) */
438                for (i0 = ipos[0]; i0 < L_CODE; i0 += STEP)
439                {
440                    if (dn2[i0] >= 0)
441                    {
442                        ps0 = dn[i0];
443
444                        /* alp0 = L_mult(rr[i0][i0],_1_4, pOverflow); */
445                        alp0 = (Word32) rr[i0][i0] << 14;
446
447                        /*----------------------------------------------------------------*
448                         * i1 loop: 8 positions.                                          *
449                         *----------------------------------------------------------------*/
450
451                        sq = -1;
452                        alp = 1;
453                        ps = 0;
454                        ix = ipos[1];
455
456                        /* initialize 4 index for next loop. */
457                        /*-------------------------------------------------------------------*
458                         *  These index have low complexity address computation because      *
459                         *  they are, in fact, pointers with fixed increment.  For example,  *
460                         *  "rr[i0][i2]" is a pointer initialized to "&rr[i0][ipos[2]]"      *
461                         *  and incremented by "STEP".                                       *
462                         *-------------------------------------------------------------------*/
463
464                        for (i1 = ipos[1]; i1 < L_CODE; i1 += STEP)
465                        {
466                            /* idx increment = STEP */
467                            /* ps1 = add(ps0, dn[i1], pOverflow); */
468                            ps1 = ps0 + dn[i1];
469
470                            /* alp1 = alp0 + rr[i0][i1] + 1/2*rr[i1][i1]; */
471
472                            /* idx incr = STEP */
473                            /* alp1 = L_mac(alp0, rr[i1][i1], _1_4, pOverflow); */
474                            alp1 = alp0 + ((Word32) rr[i1][i1] << 14);
475
476                            /* idx incr = STEP */
477                            /* alp1 = L_mac(alp1, rr[i0][i1], _1_2, pOverflow); */
478                            alp1 += (Word32) rr[i0][i1] << 15;
479
480                            /* sq1 = mult(ps1, ps1, pOverflow); */
481                            sq1 = (Word16)(((Word32) ps1 * ps1) >> 15);
482
483                            /* alp_16 = pv_round(alp1, pOverflow); */
484                            alp_16 = (Word16)((alp1 + (Word32) 0x00008000L) >> 16);
485
486                            /* s = L_mult(alp, sq1, pOverflow); */
487                            s = ((Word32) alp * sq1) << 1;
488
489                            /* s = L_msu(s, sq, alp_16, pOverflow); */
490                            s -= (((Word32) sq * alp_16) << 1);
491
492                            if (s > 0)
493                            {
494                                sq = sq1;
495                                ps = ps1;
496                                alp = alp_16;
497                                ix = i1;
498                            }
499                        }
500                        i1 = ix;
501
502                        /*----------------------------------------------------------------*
503                         * i2 loop: 8 positions.                                          *
504                         *----------------------------------------------------------------*/
505
506                        ps0 = ps;
507
508                        /* alp0 = L_mult(alp, _1_4, pOverflow); */
509                        alp0 = (Word32) alp << 14;
510
511                        sq = -1;
512                        alp = 1;
513                        ps = 0;
514                        ix = ipos[2];
515
516                        /* initialize 4 index for next loop (see i1 loop) */
517
518                        for (i2 = ipos[2]; i2 < L_CODE; i2 += STEP)
519                        {
520                            /* index increment = STEP */
521                            /* ps1 = add(ps0, dn[i2], pOverflow); */
522                            ps1 = ps0 + dn[i2];
523
524                            /* alp1 = alp0 + rr[i0][i2] + rr[i1][i2] + 1/2*rr[i2][i2]; */
525
526                            /* idx incr = STEP */
527                            /* alp1 = L_mac(alp0, rr[i2][i2], _1_16, pOverflow); */
528                            alp1 = alp0 + ((Word32) rr[i2][i2] << 12);
529
530                            /* idx incr = STEP */
531                            /* alp1 = L_mac(alp1, rr[i1][i2], _1_8, pOverflow); */
532                            alp1 += (Word32) rr[i1][i2] << 13;
533
534                            /* idx incr = STEP */
535                            /* alp1 = L_mac(alp1,rr[i0][i2], _1_8, pOverflow); */
536                            alp1 += (Word32) rr[i0][i2] << 13;
537
538                            /* sq1 = mult(ps1, ps1, pOverflow); */
539                            sq1 = (Word16)(((Word32) ps1 * ps1) >> 15);
540
541                            /* alp_16 = pv_round(alp1, pOverflow); */
542                            alp_16 = (Word16)((alp1 + (Word32) 0x00008000L) >> 16);
543
544                            /* s = L_mult(alp, sq1, pOverflow); */
545                            s = ((Word32) alp * sq1) << 1;
546
547                            /* s = L_msu(s, sq, alp_16, pOverflow); */
548                            s -= (((Word32) sq * alp_16) << 1);
549
550                            if (s > 0)
551                            {
552                                sq = sq1;
553                                ps = ps1;
554                                alp = alp_16;
555                                ix = i2;
556                            }
557                        }
558                        i2 = ix;
559
560                        /* memorize codevector if this one
561                         * is better than the last one.
562                         */
563
564                        s = L_mult(alpk, sq, pOverflow);
565                        //s = ((Word32) alpk * sq) << 1;
566
567                        s = L_msu(s, psk, alp, pOverflow);
568                        //s -= (((Word32) psk * alp) << 1);
569
570                        if (s > 0)
571                        {
572                            psk = sq;
573                            alpk = alp;
574                            p_codvec = &codvec[0];
575
576                            *(p_codvec++) = i0;
577                            *(p_codvec++) = i1;
578                            *(p_codvec) = i2;
579                        }
580                    }
581                }
582                /*----------------------------------------------------------------*
583                 * Cyclic permutation of i0, i1 and i2.                           *
584                 *----------------------------------------------------------------*/
585
586                pos = ipos[2];
587                ipos[2] = ipos[1];
588                ipos[1] = ipos[0];
589                ipos[0] = pos;
590            }
591        }
592    }
593    return;
594}
595
596/****************************************************************************/
597
598/*
599------------------------------------------------------------------------------
600 FUNCTION NAME:  build_code()
601------------------------------------------------------------------------------
602 INPUT AND OUTPUT DEFINITIONS
603
604 Inputs:
605    codvec[]   Array of type Word16 -- position of pulses
606    dn_sign[]  Array of type Word16 -- sign of pulses
607    h[]        Array of type Word16 -- impulse response of
608                                       weighted synthesis filter
609
610 Outputs:
611    cod[]  Array of type Word16 -- innovative code vector
612    y[]    Array of type Word16 -- filtered innovative code
613    sign[] Array of type Word16 -- sign of 3 pulses
614    pOverflow  Pointer to Flag  -- set when overflow occurs
615
616 Returns:
617    indx
618
619 Global Variables Used:
620    None
621
622 Local Variables Needed:
623    None
624
625------------------------------------------------------------------------------
626 FUNCTION DESCRIPTION
627
628 PURPOSE: Builds the codeword, the filtered codeword and index of the
629          codevector, based on the signs and positions of 3 pulses.
630
631------------------------------------------------------------------------------
632 REQUIREMENTS
633
634 None
635
636------------------------------------------------------------------------------
637 REFERENCES
638
639 [1] c3_14pf.c, UMTS GSM AMR speech codec, R99 - Version 3.2.0, March 2, 2001
640
641------------------------------------------------------------------------------
642 PSEUDO-CODE
643
644------------------------------------------------------------------------------
645 RESOURCES USED [optional]
646
647 When the code is written for a specific target processor the
648 the resources used should be documented below.
649
650 HEAP MEMORY USED: x bytes
651
652 STACK MEMORY USED: x bytes
653
654 CLOCK CYCLES: (cycle count equation for this function) + (variable
655                used to represent cycle count for each subroutine
656                called)
657     where: (cycle count variable) = cycle count for [subroutine
658                                     name]
659
660------------------------------------------------------------------------------
661 CAUTION [optional]
662 [State any special notes, constraints or cautions for users of this function]
663
664------------------------------------------------------------------------------
665*/
666
667static Word16
668build_code(
669    Word16 codvec[],  /* i : position of pulses                            */
670    Word16 dn_sign[], /* i : sign of pulses                                */
671    Word16 cod[],     /* o : innovative code vector                        */
672    Word16 h[],       /* i : impulse response of weighted synthesis filter */
673    Word16 y[],       /* o : filtered innovative code                      */
674    Word16 sign[],    /* o : sign of 3 pulses                              */
675    Flag  *pOverflow  /* o : Flag set when overflow occurs                 */
676)
677{
678    Word16 i;
679    Word16 j;
680    Word16 k;
681    Word16 track;
682    Word16 index;
683    Word16 _sign[NB_PULSE];
684    Word16 indx;
685    Word16 rsign;
686
687    Word16 *p0;
688    Word16 *p1;
689    Word16 *p2;
690
691    Word32 s;
692
693    for (i = 0; i < L_CODE; i++)
694    {
695        cod[i] = 0;
696    }
697
698    indx = 0;
699    rsign = 0;
700
701    for (k = 0; k < NB_PULSE; k++)
702    {
703        i = codvec[k];  /* read pulse position */
704        j = dn_sign[i];  /* read sign           */
705
706        /* index = pos/5 */
707        /* index = mult(i, 6554, pOverflow); */
708        index = (Word16)(((Word32) i * 6554) >> 15);
709
710        /* track = pos%5 */
711        /* s = L_mult(index, 5, pOverflow); */
712        s = ((Word32) index * 5) << 1;
713
714        /* s = L_shr(s, 1, pOverflow); */
715        s >>= 1;
716
717        /* track = sub(i, (Word16) s, pOverflow); */
718        track = i - (Word16) s;
719
720        if (track == 1)
721        {
722            /* index = shl(index, 4, pOverflow); */
723            index <<= 4;
724        }
725        else if (track == 2)
726        {
727            track = 2;
728
729            /* index = shl(index, 8, pOverflow); */
730            index <<= 8;
731        }
732        else if (track == 3)
733        {
734            track = 1;
735
736            /* index = shl(index, 4, pOverflow); */
737            index <<= 4;
738
739            /* index = add(index, 8, pOverflow); */
740            index += 8;
741        }
742        else if (track == 4)
743        {
744            track = 2;
745
746            /* index = shl(index, 8, pOverflow); */
747            index <<= 8;
748
749            /* index = add(index, 128, pOverflow); */
750            index += 128;
751        }
752
753        if (j > 0)
754        {
755            cod[i] = 8191;
756            _sign[k] = 32767;
757
758            /* track = shl(1, track, pOverflow); */
759            track = 1 << track;
760
761            /* rsign = add(rsign, track, pOverflow); */
762            rsign += track;
763        }
764        else
765        {
766            cod[i] = -8192;
767            _sign[k] = (Word16) - 32768L;
768        }
769
770        /* indx = add(indx, index, pOverflow); */
771        indx += index;
772    }
773    *sign = rsign;
774
775    p0 = h - codvec[0];
776    p1 = h - codvec[1];
777    p2 = h - codvec[2];
778
779    for (i = 0; i < L_CODE; i++)
780    {
781        s = 0;
782        s =
783            L_mac(
784                s,
785                *p0++,
786                _sign[0],
787                pOverflow);
788
789        s =
790            L_mac(
791                s,
792                *p1++,
793                _sign[1],
794                pOverflow);
795
796        s =
797            L_mac(
798                s,
799                *p2++,
800                _sign[2],
801                pOverflow);
802
803        y[i] =
804            pv_round(
805                s,
806                pOverflow);
807    }
808
809    return indx;
810}
811
812
813
814
815
816
817
818