/* * stradd.c * * Copyright (C) 2003 Andrew Allison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *The author may be contacted at freevms@sympatico.ca * * Andrew Allison * 50 Denlaw Road * London, Ont * Canada * N6G 3L4 * */ /* * * History * * Oct 10, 1996 - Kevin Handy * Preliminary design. Spelling errors are * not my fault! Someone must have snuck them in * there when I wasn't looking. * * Feb 4, 1997 - Kevin Handy * Added a 'return STR$_ILLSTRCLA' so that compiling * with '-Wall' won't display errors. * * Sep 10, 2003 - Andrew Allison * Wrote str$add code * * Feb 19 2004 - Andrew Allison * Changed malloc to calloc to initialize memory * * Mar 28, 2004 - Andrew Allison * Compatability testing * Added code to cause "stack dump" if c descriptor * is not NULL */ /************************************************************* * str$add * * Add two decimal strings of digits * * Fixed length output string results are blank padded or truncated * Varying length output length is set or truncated * Dynamic length output length is set * * Format * sign,exp, digits, sign,exp,digits, sign,exp,digits * * 1, 23, +12345, 0, -34,5432112, out,out,out * * Input * digits 65,536 string portion of number * exp 2,147,483,648 power of 10 to obtain value of number * sign sign of number 0 pos 1 neg * Total 2,147,549,184 * * value = sign digits * 10 ^^ exp * Returns * STR$_NORMAL * STR_TRU Truncation * Signal * LIB$_INVARG Invalid Argument * STR$_FATINTERR Internal Error * STR$_ILLSTRCLA Illegal string Class * STR$_INSVIRMEM Insufficient virtual memory * STR$_WRONUMARG Wrong number of arguments * Bugs * You could create a much more elegant solution seeing if * the numbers actually overlap befor going down the brute * force road */ #include <stdio.h> #include <string.h> #include <assert.h> #include <values.h> #include "descrip.h" #include <stdlib.h> #include "strdef.h" #include "libdef.h" #include "str$routines.h" #include "stdint.h" #define MAXSTR 132000 #define MAXUINT16 65536 /*****************************/ unsigned long str$add ( const unsigned long *asign, const long *aexp, const struct dsc$descriptor_s *adigits, const unsigned long *bsign, const long *bexp, const struct dsc$descriptor_s *bdigits, unsigned long *csign, long *cexp, struct dsc$descriptor_s *cdigits) { unsigned short s1_len, s2_len, s3_len, temp_len; char *s1_ptr, *s2_ptr, *s3_ptr; unsigned long index,max_len,min_len; int i,j,k; unsigned long status; signed long min_exp,max_exp, a_size, b_size, max_size, min_size; char ctemp; int sum,carry; char *a,*b,*c; status = STR$_NORMAL; index = 0; a = (char *) calloc(MAXSTR,1); b = (char *) calloc(MAXSTR,1); c = (char *) calloc(MAXSTR,1); if ( a == NULL ) { status = STR$_INSVIRMEM; } if ( b == NULL ) { status = STR$_INSVIRMEM; } if ( c == NULL ) { status = STR$_INSVIRMEM; } // Check the sign field is 1 or 0 if ( *asign == 1 || *asign == 0 ) ; else status = LIB$_INVARG; if ( *bsign == 1 || *bsign == 0) ; else status = LIB$_INVARG; // If we have a negative sign then call str$subtract // c = -a + b if (( *asign == 1 ) && (*bsign == 0 )) { status = str$sub(asign,aexp,adigits,bsign,bexp,bdigits,csign,cexp,cdigits); return status; } // c = a - b if (( *asign == 0 ) && (*bsign == 1 )) { status = str$sub(asign,aexp,adigits,bsign,bexp,bdigits,csign,cexp,cdigits); return status; } // c = -a + -b *csign = 0; if (( *asign == 1 ) && ( *bsign == 1)) { *csign = 1; } // Get the length of the input strings and how much room for the output str$analyze_sdesc (adigits, &s1_len, &s1_ptr); str$analyze_sdesc (bdigits, &s2_len, &s2_ptr); str$analyze_sdesc (cdigits, &s3_len, &s3_ptr); if ( s3_ptr != NULL ) { str$free1_dx (cdigits); printf ("Destination must be NULL\n"); return STR$_FATINTERR; } // Quick abort if (status != STR$_NORMAL) { return status; } // Move in the largest number - we need to keep the alignment correct // char string is "right to left" alignment // start at location specified by the exponent max_exp = ( *aexp > *bexp ) ? *aexp : *bexp; // get largest exp min_exp = ( *aexp > *bexp ) ? *bexp : *aexp; max_len = ( s1_len > s2_len ) ? s1_len : s2_len; min_len = ( s1_len > s2_len) ? s2_len : s1_len; a_size = ( *aexp + s1_len ); b_size = ( *bexp + s2_len ); max_size= ( a_size > b_size ) ? a_size : b_size; min_size= ( a_size > b_size ) ? b_size : a_size; // The strings don't overlap just return the largest if ( max_size - min_size > UINT16_MAX ) { //Don't Overlap returning largest if ( *aexp > *bexp ) { *cexp = *aexp; str$copy_dx (cdigits,adigits); } else { *cexp = *bexp; str$copy_dx(cdigits,bdigits); } return STR$_TRU; } // Copy input strings to working storage for (i = 0; i < s1_len; i++ ) { a[i] = s1_ptr[i]; } for (j = 0; j < s2_len; j++ ) { b[j] = s2_ptr[j]; } // Set the output exponent *cexp = min_exp; // Add zero's to the end of the number for remaining exponent if ( *aexp > *bexp ) { for ( i = s1_len; i < s1_len + max_exp - min_exp; i++) a[i] = '0'; s1_len += max_exp - min_exp; } if ( *aexp < *bexp ) { for ( i = s2_len; i < s2_len + max_exp - min_exp; i++) b[i] = '0'; s2_len += max_exp - min_exp; } sum = 0; carry = 0; ctemp = '0'; i = s1_len; j = s2_len; // New max string length max_len = ( s1_len > s2_len ) ? s1_len : s2_len ; for (k =(int) max_len; k > 0; k-- ) { if ( i > 0 ) { sum += a[i-1] - '0'; } if ( j > 0 ) { sum += b[j-1] - '0'; } sum += carry; carry = 0; if ( sum > 9 ) { carry = 1; sum -= 10; } ctemp = sum + '0'; sum = 0; c[k-1] = ctemp; i--; j--; } if ( carry == 1 ) { for (i = max_len-1; i >= 0; i-- ) { c[i+1] = c[i]; } c[0] = (char) (carry + '0'); max_len++; } // Truncate output sum string to 65536 MAXUINT16 if ( max_len > MAXUINT16 ) { status = STR$_TRU; max_len = MAXUINT16; } // Free any memory that is passed into us. temp_len = max_len; str$free1_dx(cdigits); str$get1_dx(&temp_len,cdigits); str$analyze_sdesc (cdigits,&s3_len,&s3_ptr); for (i = 0; i < max_len; i++) { s3_ptr[i] = c[i]; } free (a); free (b); free (c); str$$lzerotrim (&*cdigits); str$$rzerotrim (&*cdigits,&*cexp); str$$iszerotrim (&*cdigits,&*cexp); return status; } /*************************************************************/