/*

 File: s2i.cpp, 2004/07/29

 CoStLy (COmplex interval STandard functions LibrarY), Version 2.0.4

 Copyright (C) Markus Neher, markus.neher@math.uni-karlsruhe.de
               Ingo Eble,    ingoeble@web.de

 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., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "s2i.h"
#include "error.h"
#include "adaptintval.h"

void s2i(const std::string& s,Interval& enclosure)
/*
  Conversion of string constant to interval.
  
  The string is supposed to be in the scientific real number format.
  The purpose of the program is to convert a syntactically correct string
  that contains a representable number into an interval constant.

  Other strings are neither encouraged nor supported and may evoke all 
  sorts of funny effects. 
*/  
{
  /*
    Conversion of string 

    {+,-} xx····xxx.yyy···y {E,e}{+,-} zzzz

    with ciphers x,y,z. The blank spaces are only for illustration.
  */

  if( !s.empty() ) //String is not empty.
    {      
      /* 
         Is there a sign?

         {+,-} xx····xxx.yyy···y {E,e}{+,-} zzzz
          ^^^
         
         '+' is represented by ASCII code 43, '-' by ASCII code 45.
         If the first character is '+' or '-' then "vz" is set to 1 or -1, 
         respectively.
         
         "vzind" is a pointer to the first position of the number. If there is a
         sign then  vzind  is set to 1, otherwise to 0.

         Incorrecet strings like "+-+212" will result in an error.
      */
         
      int vz = 44-int(s[0]), vzind = abs(vz);
      if( vzind != 1 ) // No sign.
        {              // vz != 1 und vz != -1 <=> abs(vz) != 1 <=> vzind != 1
          vzind = 0;
          vz    = 1;
        }

      /*
        Is there an exponent?

        {+,-} xx····xxx.yyy···y {E,e}{+,-} zzzz
                                 ^^^  ^^^  ^^^^

        "pos_exp" is set to the first position of the characters 'e' or 'E' 
        in the string. If the string doesn't contain these literals then 
        pos_exp is set to -1.
        
        If the string starts with 'e' or 'E' then  pos_exp  is first set to 0,
        but this is corrected to the length of the string. Incorrect strings 
        like "E+xxx" yield the result [0,0].
        
        In all other cases the exponent is read. Incorrect exponent strings 
        like "12.34efgd" yield incorrect results.
      */

      int pos_exp = s.find_first_of( "eE" ); //Get position of exponent.
      int exp = 0;
      if( pos_exp > 0 ) 
        {
          std::string exponent( s.substr( pos_exp+1, s.size() ) );
          std::string expstr( s.substr( pos_exp+2, s.size() ) );
	  
	  /*
	    Syntax check.
	  */

	  int check = expstr.find_first_not_of( "+-0123456789" );
	  if( check >= 0 || !(s[pos_exp+1] == '+' || s[pos_exp+1] == '-') ) 
	    throw syntax_error("What is the meaning of '" + s + "'?");;
	    
          exp = atoi( exponent.c_str() );
      }
      else pos_exp = s.size();

      /*
        Is there a dezimal point?

        {+,-} xx····xxx.yyy···y {E,e}{+,-} zzzz
                       ^

        If there is no dezimal point then the full mantissa is read.
        Otherwise, the mantissa is normalized such that the dezimal point 
        is removed and the exponent is changed accordingly.
      */

      std::string mantissa;
      int pos_point = s.find( '.' ); // Get position of point '.'
      if( pos_point == -1 ) mantissa= s.substr( vzind, pos_exp-vzind );
      else
        {
          exp -= pos_exp - 1 - pos_point; // Change exponent.
          mantissa = s.substr( vzind, pos_point-vzind ) + s.substr( pos_point+1, pos_exp-pos_point-1 );
        }

      /*
	Syntax check
      */

      int check = mantissa.find_first_not_of( "0123456789" );
      if( check >= 0 ) throw syntax_error("What is the meaning of '" + s + "'?");;

      /*
        Read mantissa

           mantissa = xx····xxxyyy···y

        Start at leftmost position. Read blocks of  "len_m"  characters, where 
        len_m  is the minimum of 9 and the length of the remaining mantissa
        (blocks of 9 dezimal digits are exactly representable as intergers).
        
        Each block is accompanied by its respective exponent relative to the
        last position ("pos_m+=len_m", "ppmz-=len_m"). The exponent "exp" is then
        added and "read block * [10,10]^exponent" is the calculated interval
        enclosure of the current block, which is added to "enclosure".
      */

      enclosure = 0;
      int pos_m = 0;
      int ppmz  = mantissa.length();

      // Read first block of characters.
      int len_m = ( 9 < ppmz ) ? 9 : ppmz;
      std::string sub = mantissa.substr( pos_m, len_m );
      pos_m += len_m;
      ppmz  -= len_m; // Update.
      enclosure += atoi( sub.c_str() );

      // Read remaining digits in the mantissa.
      while( ppmz > 0 )
        {
          // Read block of at most 9 characters.
          len_m = ( 9 < ppmz ) ? 9 : ppmz;
          std::string sub = mantissa.substr( pos_m, len_m );
          pos_m += len_m;
          ppmz  -= len_m; // Update.

          // Multiply with exponent.
          if( len_m == 9 )
            enclosure *= 1000000000;
          else
            {
              // Assumption: len_m >=1! Empty strings are not considered here!
              int fact = 10;
              while( len_m > 1)
                {
                  fact *=10;
                  len_m--;
                }
              enclosure *= fact;
            }

          //Add mantissa.
          enclosure += atoi( sub.c_str() );
        }

      // The mantissa has been stored in enclosure.
      // Now compute 10^exp and multiply.
      // 10^15 is an exactly representable double number.

      int expo = abs( exp );
      int exp_r = expo % 15;
      expo = (expo - exp_r) / 15;

      // Multiply with remainder of exponent with respect to 15
      if( exp_r > 0 )
        {
          double dfact = 10;
          while( exp_r > 1 )
            {
              dfact *=10;
              exp_r--;
            }
          if( exp > 0 )
            enclosure *= dfact;
          else
            enclosure /= dfact;
        }

      // Multiply with remaining parts of the exponent.

      // Store 10^15 into ifact.
      double dfact = 100000;
      dfact *= 100000;
      dfact *= 100000;
      Interval ifact_l, ifact_t;
      ifact_l = dfact;
      ifact_t = 1;

      // Store 10^remainder into ifact_t.
      while( expo > 0 )
        {
          if( expo & 1 ) // 'expo' is odd.
            {
              ifact_t *= ifact_l;
              expo--;
            }
          expo >>= 1;    // Division by 2 realized with a right shift.
          ifact_l *= ifact_l;
        }

      // Multiply enclosure.
      if( exp > 0 )
        enclosure *= ifact_t;
      else
        enclosure /= ifact_t;

      // Multiply with sign & return.
      enclosure *= vz;     
    }
  else std::cerr<<"Unexpected error: Empty string."<< std::endl; //Error message. 
  //Are there expected errors?
}

/*

  End of File: s2i.cpp

*/
