source: trunk/cscrypt/bn_lib.c@ 3181

Last change on this file since 3181 was 3181, checked in by dingo35, 10 years ago

Adding threadsafety FIXMEs, feel free to join checking..

File size: 16.0 KB
Line 
1//FIXME Not checked on threadsafety yet; after checking please remove this line
2/* crypto/bn/bn_lib.c */
3/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
4 * All rights reserved.
5 *
6 * This package is an SSL implementation written
7 * by Eric Young (eay@cryptsoft.com).
8 * The implementation was written so as to conform with Netscapes SSL.
9 *
10 * This library is free for commercial and non-commercial use as long as
11 * the following conditions are aheared to. The following conditions
12 * apply to all code found in this distribution, be it the RC4, RSA,
13 * lhash, DES, etc., code; not just the SSL code. The SSL documentation
14 * included with this distribution is covered by the same copyright terms
15 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
16 *
17 * Copyright remains Eric Young's, and as such any Copyright notices in
18 * the code are not to be removed.
19 * If this package is used in a product, Eric Young should be given attribution
20 * as the author of the parts of the library used.
21 * This can be in the form of a textual message at program startup or
22 * in documentation (online or textual) provided with the package.
23 *
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
26 * are met:
27 * 1. Redistributions of source code must retain the copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. Redistributions in binary form must reproduce the above copyright
30 * notice, this list of conditions and the following disclaimer in the
31 * documentation and/or other materials provided with the distribution.
32 * 3. All advertising materials mentioning features or use of this software
33 * must display the following acknowledgement:
34 * "This product includes cryptographic software written by
35 * Eric Young (eay@cryptsoft.com)"
36 * The word 'cryptographic' can be left out if the rouines from the library
37 * being used are not cryptographic related :-).
38 * 4. If you include any Windows specific code (or a derivative thereof) from
39 * the apps directory (application code) you must include an acknowledgement:
40 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
41 *
42 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 *
54 * The licence and distribution terms for any publically available version or
55 * derivative of this code cannot be changed. i.e. this code cannot simply be
56 * copied and put under another distribution licence
57 * [including the GNU Public Licence.]
58 */
59
60#ifndef BN_DEBUG
61# undef NDEBUG /* avoid conflicting definitions */
62# define NDEBUG
63#endif
64
65#include <string.h>
66#include <assert.h>
67#include <limits.h>
68#include <stdio.h>
69#include "bn_lcl.h"
70#include "openssl_mods.h"
71
72const char *BN_version="Big Number 42";
73
74/* For a 32 bit machine
75 * 2 - 4 == 128
76 * 3 - 8 == 256
77 * 4 - 16 == 512
78 * 5 - 32 == 1024
79 * 6 - 64 == 2048
80 * 7 - 128 == 4096
81 * 8 - 256 == 8192
82 */
83static int bn_limit_bits=0;
84static int bn_limit_num=8; /* (1<<bn_limit_bits) */
85static int bn_limit_bits_low=0;
86static int bn_limit_num_low=8; /* (1<<bn_limit_bits_low) */
87static int bn_limit_bits_high=0;
88static int bn_limit_num_high=8; /* (1<<bn_limit_bits_high) */
89static int bn_limit_bits_mont=0;
90static int bn_limit_num_mont=8; /* (1<<bn_limit_bits_mont) */
91
92void BN_set_params(int mult, int high, int low, int mont)
93 {
94 if (mult >= 0)
95 {
96 if (mult > (int)(sizeof(int)*8)-1)
97 mult=sizeof(int)*8-1;
98 bn_limit_bits=mult;
99 bn_limit_num=1<<mult;
100 }
101 if (high >= 0)
102 {
103 if (high > (int)(sizeof(int)*8)-1)
104 high=sizeof(int)*8-1;
105 bn_limit_bits_high=high;
106 bn_limit_num_high=1<<high;
107 }
108 if (low >= 0)
109 {
110 if (low > (int)(sizeof(int)*8)-1)
111 low=sizeof(int)*8-1;
112 bn_limit_bits_low=low;
113 bn_limit_num_low=1<<low;
114 }
115 if (mont >= 0)
116 {
117 if (mont > (int)(sizeof(int)*8)-1)
118 mont=sizeof(int)*8-1;
119 bn_limit_bits_mont=mont;
120 bn_limit_num_mont=1<<mont;
121 }
122 }
123
124int BN_get_params(int which)
125 {
126 if (which == 0) return(bn_limit_bits);
127 else if (which == 1) return(bn_limit_bits_high);
128 else if (which == 2) return(bn_limit_bits_low);
129 else if (which == 3) return(bn_limit_bits_mont);
130 else return(0);
131 }
132
133const BIGNUM *BN_value_one(void)
134 {
135 static const BN_ULONG data_one=1L;
136 static const BIGNUM const_one={(BN_ULONG *)&data_one,1,1,0,BN_FLG_STATIC_DATA};
137
138 return(&const_one);
139 }
140
141char *BN_options(void)
142 {
143 static int init=0;
144 static char data[16];
145
146 if (!init)
147 {
148 init++;
149#ifdef BN_LLONG
150 sprintf(data,"bn(%d,%d)",(int)sizeof(BN_ULLONG)*8,
151 (int)sizeof(BN_ULONG)*8);
152#else
153 sprintf(data,"bn(%d,%d)",(int)sizeof(BN_ULONG)*8,
154 (int)sizeof(BN_ULONG)*8);
155#endif
156 }
157 return(data);
158 }
159
160int BN_num_bits_word(BN_ULONG l)
161 {
162 static const char bits[256]={
163 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,
164 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
165 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
166 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
167 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
168 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
169 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
170 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
171 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
172 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
173 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
174 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
175 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
176 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
177 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
178 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
179 };
180
181#if defined(SIXTY_FOUR_BIT_LONG)
182 if (l & 0xffffffff00000000L)
183 {
184 if (l & 0xffff000000000000L)
185 {
186 if (l & 0xff00000000000000L)
187 {
188 return(bits[(int)(l>>56)]+56);
189 }
190 else return(bits[(int)(l>>48)]+48);
191 }
192 else
193 {
194 if (l & 0x0000ff0000000000L)
195 {
196 return(bits[(int)(l>>40)]+40);
197 }
198 else return(bits[(int)(l>>32)]+32);
199 }
200 }
201 else
202#else
203#ifdef SIXTY_FOUR_BIT
204 if (l & 0xffffffff00000000LL)
205 {
206 if (l & 0xffff000000000000LL)
207 {
208 if (l & 0xff00000000000000LL)
209 {
210 return(bits[(int)(l>>56)]+56);
211 }
212 else return(bits[(int)(l>>48)]+48);
213 }
214 else
215 {
216 if (l & 0x0000ff0000000000LL)
217 {
218 return(bits[(int)(l>>40)]+40);
219 }
220 else return(bits[(int)(l>>32)]+32);
221 }
222 }
223 else
224#endif
225#endif
226 {
227#if defined(THIRTY_TWO_BIT) || defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG)
228 if (l & 0xffff0000L)
229 {
230 if (l & 0xff000000L)
231 return(bits[(int)(l>>24L)]+24);
232 else return(bits[(int)(l>>16L)]+16);
233 }
234 else
235#endif
236 {
237#if defined(SIXTEEN_BIT) || defined(THIRTY_TWO_BIT) || defined(SIXTY_FOUR_BIT) || defined(SIXTY_FOUR_BIT_LONG)
238 if (l & 0xff00L)
239 return(bits[(int)(l>>8)]+8);
240 else
241#endif
242 return(bits[(int)(l )] );
243 }
244 }
245 }
246
247int BN_num_bits(const BIGNUM *a)
248 {
249 BN_ULONG l;
250 int i;
251
252 bn_check_top(a);
253
254 if (a->top == 0) return(0);
255 l=a->d[a->top-1];
256 assert(l != 0);
257 i=(a->top-1)*BN_BITS2;
258 return(i+BN_num_bits_word(l));
259 }
260
261void BN_clear_free(BIGNUM *a)
262 {
263 int i;
264
265 if (a == NULL) return;
266 if (a->d != NULL)
267 {
268 memset(a->d,0,a->dmax*sizeof(a->d[0]));
269 if (!(BN_get_flags(a,BN_FLG_STATIC_DATA)))
270 OPENSSL_free(a->d);
271 }
272 i=BN_get_flags(a,BN_FLG_MALLOCED);
273 memset(a,0,sizeof(BIGNUM));
274 if (i)
275 OPENSSL_free(a);
276 }
277
278void BN_free(BIGNUM *a)
279 {
280 if (a == NULL) return;
281 if ((a->d != NULL) && !(BN_get_flags(a,BN_FLG_STATIC_DATA)))
282 OPENSSL_free(a->d);
283 a->flags|=BN_FLG_FREE; /* REMOVE? */
284 if (a->flags & BN_FLG_MALLOCED)
285 OPENSSL_free(a);
286 }
287
288void BN_init(BIGNUM *a)
289 {
290 memset(a,0,sizeof(BIGNUM));
291 }
292
293BIGNUM *BN_new(void)
294 {
295 BIGNUM *ret;
296
297 if ((ret=(BIGNUM *)OPENSSL_malloc(sizeof(BIGNUM))) == NULL)
298 {
299 return(NULL);
300 }
301 ret->flags=BN_FLG_MALLOCED;
302 ret->top=0;
303 ret->neg=0;
304 ret->dmax=0;
305 ret->d=NULL;
306 return(ret);
307 }
308
309/* This is an internal function that should not be used in applications.
310 * It ensures that 'b' has enough room for a 'words' word number number.
311 * It is mostly used by the various BIGNUM routines. If there is an error,
312 * NULL is returned. If not, 'b' is returned. */
313
314BIGNUM *bn_expand2(BIGNUM *b, int words)
315 {
316 BN_ULONG *A,*a;
317 const BN_ULONG *B;
318 int i;
319
320 bn_check_top(b);
321
322 if (words > b->dmax)
323 {
324 if (words > (INT_MAX/(4*BN_BITS2)))
325 {
326 return NULL;
327 }
328
329 bn_check_top(b);
330 if (BN_get_flags(b,BN_FLG_STATIC_DATA))
331 {
332 return(NULL);
333 }
334 a=A=(BN_ULONG *)OPENSSL_malloc(sizeof(BN_ULONG)*(words+1));
335 if (A == NULL)
336 {
337 return(NULL);
338 }
339#if 1
340 B=b->d;
341 /* Check if the previous number needs to be copied */
342 if (B != NULL)
343 {
344#if 0
345 /* This lot is an unrolled loop to copy b->top
346 * BN_ULONGs from B to A
347 */
348/*
349 * I have nothing against unrolling but it's usually done for
350 * several reasons, namely:
351 * - minimize percentage of decision making code, i.e. branches;
352 * - avoid cache trashing;
353 * - make it possible to schedule loads earlier;
354 * Now let's examine the code below. The cornerstone of C is
355 * "programmer is always right" and that's what we love it for:-)
356 * For this very reason C compilers have to be paranoid when it
357 * comes to data aliasing and assume the worst. Yeah, but what
358 * does it mean in real life? This means that loop body below will
359 * be compiled to sequence of loads immediately followed by stores
360 * as compiler assumes the worst, something in A==B+1 style. As a
361 * result CPU pipeline is going to starve for incoming data. Secondly
362 * if A and B happen to share same cache line such code is going to
363 * cause severe cache trashing. Both factors have severe impact on
364 * performance of modern CPUs and this is the reason why this
365 * particular piece of code is #ifdefed away and replaced by more
366 * "friendly" version found in #else section below. This comment
367 * also applies to BN_copy function.
368 *
369 * <appro@fy.chalmers.se>
370 */
371 for (i=b->top&(~7); i>0; i-=8)
372 {
373 A[0]=B[0]; A[1]=B[1]; A[2]=B[2]; A[3]=B[3];
374 A[4]=B[4]; A[5]=B[5]; A[6]=B[6]; A[7]=B[7];
375 A+=8;
376 B+=8;
377 }
378 switch (b->top&7)
379 {
380 case 7:
381 A[6]=B[6];
382 case 6:
383 A[5]=B[5];
384 case 5:
385 A[4]=B[4];
386 case 4:
387 A[3]=B[3];
388 case 3:
389 A[2]=B[2];
390 case 2:
391 A[1]=B[1];
392 case 1:
393 A[0]=B[0];
394 case 0:
395 /* I need the 'case 0' entry for utrix cc.
396 * If the optimizer is turned on, it does the
397 * switch table by doing
398 * a=top&7
399 * a--;
400 * goto jump_table[a];
401 * If top is 0, this makes us jump to 0xffffffc
402 * which is rather bad :-(.
403 * eric 23-Apr-1998
404 */
405 ;
406 }
407#else
408 for (i=b->top>>2; i>0; i--,A+=4,B+=4)
409 {
410 /*
411 * The fact that the loop is unrolled
412 * 4-wise is a tribute to Intel. It's
413 * the one that doesn't have enough
414 * registers to accomodate more data.
415 * I'd unroll it 8-wise otherwise:-)
416 *
417 * <appro@fy.chalmers.se>
418 */
419 BN_ULONG a0,a1,a2,a3;
420 a0=B[0]; a1=B[1]; a2=B[2]; a3=B[3];
421 A[0]=a0; A[1]=a1; A[2]=a2; A[3]=a3;
422 }
423 switch (b->top&3)
424 {
425 case 3: A[2]=B[2];
426 case 2: A[1]=B[1];
427 case 1: A[0]=B[0];
428 case 0: ; /* ultrix cc workaround, see above */
429 }
430#endif
431 OPENSSL_free(b->d);
432 }
433
434 b->d=a;
435 b->dmax=words;
436
437 /* Now need to zero any data between b->top and b->max */
438
439 A= &(b->d[b->top]);
440 for (i=(b->dmax - b->top)>>3; i>0; i--,A+=8)
441 {
442 A[0]=0; A[1]=0; A[2]=0; A[3]=0;
443 A[4]=0; A[5]=0; A[6]=0; A[7]=0;
444 }
445 for (i=(b->dmax - b->top)&7; i>0; i--,A++)
446 A[0]=0;
447#else
448 memset(A,0,sizeof(BN_ULONG)*(words+1));
449 memcpy(A,b->d,sizeof(b->d[0])*b->top);
450 b->d=a;
451 b->max=words;
452#endif
453
454/* memset(&(p[b->max]),0,((words+1)-b->max)*sizeof(BN_ULONG)); */
455/* { int i; for (i=b->max; i<words+1; i++) p[i]=i;} */
456
457 }
458 return(b);
459 }
460
461BIGNUM *BN_dup(const BIGNUM *a)
462 {
463 BIGNUM *r;
464
465 if (a == NULL) return NULL;
466
467 bn_check_top(a);
468
469 r=BN_new();
470 if (r == NULL) return(NULL);
471 return((BIGNUM *)BN_copy(r,a));
472 }
473
474BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b)
475 {
476 int i;
477 BN_ULONG *A;
478 const BN_ULONG *B;
479
480 bn_check_top(b);
481
482 if (a == b) return(a);
483 if (bn_wexpand(a,b->top) == NULL) return(NULL);
484
485#if 1
486 A=a->d;
487 B=b->d;
488 for (i=b->top>>2; i>0; i--,A+=4,B+=4)
489 {
490 BN_ULONG a0,a1,a2,a3;
491 a0=B[0]; a1=B[1]; a2=B[2]; a3=B[3];
492 A[0]=a0; A[1]=a1; A[2]=a2; A[3]=a3;
493 }
494 switch (b->top&3)
495 {
496 case 3: A[2]=B[2];
497 case 2: A[1]=B[1];
498 case 1: A[0]=B[0];
499 case 0: ; /* ultrix cc workaround, see comments in bn_expand2 */
500 }
501#else
502 memcpy(a->d,b->d,sizeof(b->d[0])*b->top);
503#endif
504
505/* memset(&(a->d[b->top]),0,sizeof(a->d[0])*(a->max-b->top));*/
506 a->top=b->top;
507 if ((a->top == 0) && (a->d != NULL))
508 a->d[0]=0;
509 a->neg=b->neg;
510 return(a);
511 }
512
513void BN_clear(BIGNUM *a)
514 {
515 if (a->d != NULL)
516 memset(a->d,0,a->dmax*sizeof(a->d[0]));
517 a->top=0;
518 a->neg=0;
519 }
520
521BN_ULONG BN_get_word(const BIGNUM *a)
522 {
523 if (a->top > 1)
524 return BN_MASK2;
525 else if (a->top == 1)
526 return a->d[0];
527 /* a->top == 0 */
528 return 0;
529 }
530
531int BN_set_word(BIGNUM *a, BN_ULONG w)
532 {
533 bn_check_top(a);
534 if (bn_expand(a,(int)sizeof(BN_ULONG)*8) == NULL) return(0);
535 a->neg = 0;
536 a->d[0] = w;
537 a->top = (w ? 1 : 0);
538 bn_check_top(a);
539 return(1);
540 }
541
542/* ignore negative */
543BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
544 {
545 unsigned int i,m;
546 unsigned int n;
547 BN_ULONG l;
548
549 if (ret == NULL) ret=BN_new();
550 if (ret == NULL) return(NULL);
551 l=0;
552 n=len;
553 if (n == 0)
554 {
555 ret->top=0;
556 return(ret);
557 }
558 if (bn_expand(ret,(int)(n+2)*8) == NULL)
559 return(NULL);
560 i=((n-1)/BN_BYTES)+1;
561 m=((n-1)%(BN_BYTES));
562 ret->top=i;
563 while (n-- > 0)
564 {
565 l=(l<<8L)| *(s++);
566 if (m-- == 0)
567 {
568 ret->d[--i]=l;
569 l=0;
570 m=BN_BYTES-1;
571 }
572 }
573 /* need to call this due to clear byte at top if avoiding
574 * having the top bit set (-ve number) */
575 bn_fix_top(ret);
576 return(ret);
577 }
578
579/* ignore negative */
580int BN_bn2bin(const BIGNUM *a, unsigned char *to)
581 {
582 int n,i;
583 BN_ULONG l;
584
585 n=i=BN_num_bytes(a);
586 while (i-- > 0)
587 {
588 l=a->d[i/BN_BYTES];
589 *(to++)=(unsigned char)(l>>(8*(i%BN_BYTES)))&0xff;
590 }
591 return(n);
592 }
593
594int BN_ucmp(const BIGNUM *a, const BIGNUM *b)
595 {
596 int i;
597 BN_ULONG t1,t2,*ap,*bp;
598
599 bn_check_top(a);
600 bn_check_top(b);
601
602 i=a->top-b->top;
603 if (i != 0) return(i);
604 ap=a->d;
605 bp=b->d;
606 for (i=a->top-1; i>=0; i--)
607 {
608 t1= ap[i];
609 t2= bp[i];
610 if (t1 != t2)
611 return(t1 > t2?1:-1);
612 }
613 return(0);
614 }
615
616int BN_cmp(const BIGNUM *a, const BIGNUM *b)
617 {
618 int i;
619 int gt,lt;
620 BN_ULONG t1,t2;
621
622 if ((a == NULL) || (b == NULL))
623 {
624 if (a != NULL)
625 return(-1);
626 else if (b != NULL)
627 return(1);
628 else
629 return(0);
630 }
631
632 bn_check_top(a);
633 bn_check_top(b);
634
635 if (a->neg != b->neg)
636 {
637 if (a->neg)
638 return(-1);
639 else return(1);
640 }
641 if (a->neg == 0)
642 { gt=1; lt= -1; }
643 else { gt= -1; lt=1; }
644
645 if (a->top > b->top) return(gt);
646 if (a->top < b->top) return(lt);
647 for (i=a->top-1; i>=0; i--)
648 {
649 t1=a->d[i];
650 t2=b->d[i];
651 if (t1 > t2) return(gt);
652 if (t1 < t2) return(lt);
653 }
654 return(0);
655 }
656
657int BN_set_bit(BIGNUM *a, int n)
658 {
659 int i,j,k;
660
661 i=n/BN_BITS2;
662 j=n%BN_BITS2;
663 if (a->top <= i)
664 {
665 if (bn_wexpand(a,i+1) == NULL) return(0);
666 for(k=a->top; k<i+1; k++)
667 a->d[k]=0;
668 a->top=i+1;
669 }
670
671 a->d[i]|=(((BN_ULONG)1)<<j);
672 return(1);
673 }
674
675int BN_clear_bit(BIGNUM *a, int n)
676 {
677 int i,j;
678
679 i=n/BN_BITS2;
680 j=n%BN_BITS2;
681 if (a->top <= i) return(0);
682
683 a->d[i]&=(~(((BN_ULONG)1)<<j));
684 bn_fix_top(a);
685 return(1);
686 }
687
688int BN_is_bit_set(const BIGNUM *a, int n)
689 {
690 int i,j;
691
692 if (n < 0) return(0);
693 i=n/BN_BITS2;
694 j=n%BN_BITS2;
695 if (a->top <= i) return(0);
696 return((a->d[i]&(((BN_ULONG)1)<<j))?1:0);
697 }
698
699int BN_mask_bits(BIGNUM *a, int n)
700 {
701 int b,w;
702
703 w=n/BN_BITS2;
704 b=n%BN_BITS2;
705 if (w >= a->top) return(0);
706 if (b == 0)
707 a->top=w;
708 else
709 {
710 a->top=w+1;
711 a->d[w]&= ~(BN_MASK2<<b);
712 }
713 bn_fix_top(a);
714 return(1);
715 }
716
717int bn_cmp_words(BN_ULONG *a, BN_ULONG *b, int n)
718 {
719 int i;
720 BN_ULONG aa,bb;
721
722 aa=a[n-1];
723 bb=b[n-1];
724 if (aa != bb) return((aa > bb)?1:-1);
725 for (i=n-2; i>=0; i--)
726 {
727 aa=a[i];
728 bb=b[i];
729 if (aa != bb) return((aa > bb)?1:-1);
730 }
731 return(0);
732 }
733
Note: See TracBrowser for help on using the repository browser.