diff --git a/appveyor.yml b/appveyor.yml index 99f821570d..fa9564dd4c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,9 +27,11 @@ install: - cmd: for /r %%i in (*.bat) do unix2dos "%%i" - cmd: if "%PLATFORM%" == "x64" set FB_PROCESSOR_ARCHITECTURE=AMD64 - cmd: if "%PLATFORM%" == "x64" set FB_OUTPUT_SUFFIX=x64 + - cmd: if "%PLATFORM%" == "x64" set FB_VS_ARCH=amd64 - cmd: if "%PLATFORM%" == "x86" set FB_PROCESSOR_ARCHITECTURE=x86 - cmd: if "%PLATFORM%" == "x86" set FB_OUTPUT_SUFFIX=win32 - - cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" + - cmd: if "%PLATFORM%" == "x86" set FB_VS_ARCH=x86 + - cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -arch=%FB_VS_ARCH% - cmd: cd builds\win32 - cmd: run_all.bat JUSTBUILD - cmd: set ARTIFACTS_PATH=output_%FB_OUTPUT_SUFFIX% diff --git a/builds/win32/make_boot.bat b/builds/win32/make_boot.bat index b4d62b90dd..882c0829e7 100644 --- a/builds/win32/make_boot.bat +++ b/builds/win32/make_boot.bat @@ -41,6 +41,9 @@ if "%ERRLEV%"=="1" goto :END call :decNumber if "%ERRLEV%"=="1" goto :END +if "%FB_TARGET_PLATFORM%"=="x64" call :ttmath +if "%ERRLEV%"=="1" goto :END + call :re2 if "%ERRLEV%"=="1" goto :END @@ -162,6 +165,23 @@ if errorlevel 1 call :boot2 decNumber_%FB_OBJ_DIR% @call set_build_target.bat %* goto :EOF +::=================== +:: BUILD ttmath +:ttmath +@echo. +@call set_build_target.bat %* RELEASE +@echo Building ttmath (%FB_OBJ_DIR%)... +@mkdir %FB_TEMP_DIR%\..\%FB_OBJ_DIR%\common 2>nul +@ml64.exe /c /Fo %FB_TEMP_DIR%\..\%FB_OBJ_DIR%\common\ttmathuint_x86_64_msvc.obj %FB_ROOT_PATH%\extern\ttmath\ttmathuint_x86_64_msvc.asm +if errorlevel 1 call :boot2 ttmath_%FB_OBJ_DIR% +@call set_build_target.bat %* DEBUG +@echo Building decNumber (%FB_OBJ_DIR%)... +@mkdir %FB_TEMP_DIR%\..\%FB_OBJ_DIR%\common 2>nul +@ml64.exe /c /Zi /Fo %FB_TEMP_DIR%\..\%FB_OBJ_DIR%\common\ttmathuint_x86_64_msvc.obj %FB_ROOT_PATH%\extern\ttmath\ttmathuint_x86_64_msvc.asm +if errorlevel 1 call :boot2 ttmath_%FB_OBJ_DIR% +@call set_build_target.bat %* +goto :EOF + ::=================== :: BUILD re2 :re2 diff --git a/builds/win32/msvc10/common.vcxproj b/builds/win32/msvc10/common.vcxproj index 99df722d88..f8f9271413 100644 --- a/builds/win32/msvc10/common.vcxproj +++ b/builds/win32/msvc10/common.vcxproj @@ -72,6 +72,7 @@ + @@ -180,6 +181,7 @@ + @@ -335,7 +337,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;$(TargetDir)\ttmathuint_x86_64_msvc.obj;%(AdditionalDependencies) @@ -353,7 +355,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;$(TargetDir)\ttmathuint_x86_64_msvc.obj;%(AdditionalDependencies) diff --git a/builds/win32/msvc10/common.vcxproj.filters b/builds/win32/msvc10/common.vcxproj.filters index 978f26a315..f6d9bf98c0 100644 --- a/builds/win32/msvc10/common.vcxproj.filters +++ b/builds/win32/msvc10/common.vcxproj.filters @@ -240,6 +240,9 @@ common + + common + @@ -581,5 +584,8 @@ headers + + headers + \ No newline at end of file diff --git a/builds/win32/msvc12/common.vcxproj b/builds/win32/msvc12/common.vcxproj index 0492407080..8b2e9805a0 100644 --- a/builds/win32/msvc12/common.vcxproj +++ b/builds/win32/msvc12/common.vcxproj @@ -68,6 +68,7 @@ + @@ -176,6 +177,7 @@ + @@ -335,7 +337,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;$(TargetDir)\ttmathuint_x86_64_msvc.obj;%(AdditionalDependencies) @@ -353,7 +355,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;$(TargetDir)\ttmathuint_x86_64_msvc.obj;%(AdditionalDependencies) diff --git a/builds/win32/msvc12/common.vcxproj.filters b/builds/win32/msvc12/common.vcxproj.filters index 328eabca1d..ffc6d9c8fe 100644 --- a/builds/win32/msvc12/common.vcxproj.filters +++ b/builds/win32/msvc12/common.vcxproj.filters @@ -240,6 +240,9 @@ common + + common + @@ -581,5 +584,8 @@ headers + + headers + \ No newline at end of file diff --git a/builds/win32/msvc14/common.vcxproj b/builds/win32/msvc14/common.vcxproj index 77823eb058..615a314773 100644 --- a/builds/win32/msvc14/common.vcxproj +++ b/builds/win32/msvc14/common.vcxproj @@ -68,6 +68,7 @@ + @@ -177,6 +178,7 @@ + @@ -337,7 +339,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;../../../extern/re2/builds/$(PlatformName)\$(Configuration)\re2.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;../../../extern/re2/builds/$(PlatformName)\$(Configuration)\re2.lib;$(TargetDir)\ttmathuint_x86_64_msvc.obj;%(AdditionalDependencies) @@ -355,7 +357,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;../../../extern/re2/builds/$(PlatformName)\$(Configuration)\re2.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;../../../extern/re2/builds/$(PlatformName)\$(Configuration)\re2.lib;$(TargetDir)\ttmathuint_x86_64_msvc.obj;%(AdditionalDependencies) diff --git a/builds/win32/msvc14/common.vcxproj.filters b/builds/win32/msvc14/common.vcxproj.filters index a0b2d29362..4da18bbba5 100644 --- a/builds/win32/msvc14/common.vcxproj.filters +++ b/builds/win32/msvc14/common.vcxproj.filters @@ -243,6 +243,9 @@ common + + common + @@ -587,5 +590,8 @@ headers + + headers + diff --git a/builds/win32/msvc15/common.vcxproj b/builds/win32/msvc15/common.vcxproj index c2ccd9f972..e8d6f56042 100644 --- a/builds/win32/msvc15/common.vcxproj +++ b/builds/win32/msvc15/common.vcxproj @@ -68,6 +68,7 @@ + @@ -177,6 +178,7 @@ + @@ -338,7 +340,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;../../../extern/re2/builds/$(PlatformName)\$(Configuration)\re2.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;$(TargetDir)\ttmathuint_x86_64_msvc.obj;../../../extern/re2/builds/$(PlatformName)\$(Configuration)\re2.lib;%(AdditionalDependencies) @@ -356,7 +358,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;../../../extern/re2/builds/$(PlatformName)\$(Configuration)\re2.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/libtomcrypt/lib/$(PlatformName)\$(Configuration)\tomcrypt.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;../../../extern/re2/builds/$(PlatformName)\$(Configuration)\re2.lib;$(TargetDir)\ttmathuint_x86_64_msvc.obj;%(AdditionalDependencies) diff --git a/builds/win32/msvc15/common.vcxproj.filters b/builds/win32/msvc15/common.vcxproj.filters index 0944d0518d..776d4a9db7 100644 --- a/builds/win32/msvc15/common.vcxproj.filters +++ b/builds/win32/msvc15/common.vcxproj.filters @@ -243,6 +243,9 @@ common + + common + @@ -587,5 +590,8 @@ headers + + headers + \ No newline at end of file diff --git a/doc/sql.extensions/README.data_types b/doc/sql.extensions/README.data_types index 4df707dc4c..ac6dbc10b0 100644 --- a/doc/sql.extensions/README.data_types +++ b/doc/sql.extensions/README.data_types @@ -200,7 +200,7 @@ Enhancement in precision of calculations with NUMERIC/DECIMAL (FB 4.0) -------------- Function: - Maximum precision of NUMERIC and DECIMAL data types is increased to 34 digits. + Maximum precision of NUMERIC and DECIMAL data types is increased to 38 digits. Author: Alex Peshkoff @@ -208,20 +208,35 @@ Enhancement in precision of calculations with NUMERIC/DECIMAL (FB 4.0) Syntax rules: NUMERIC ( P {, N} ) DECIMAL ( P {, N} ) - where P is precision (P <= 34, was limited prior with 18 digits) and N is optional number + where P is precision (P <= 38, was limited prior with 18 digits) and N is optional number of digits after decimal separator (as before). Storage: - 128-bit, format according to IEEE 754. + 128-bit signed integer. Example(s): 1. DECLARE VARIABLE VAR1 DECIMAL(25); - 2. CREATE TABLE TABLE1 (FIELD1 NUMERIC(34, 17)); + 2. CREATE TABLE TABLE1 (FIELD1 NUMERIC(38, 19)); Note(s): Numerics with precision less than 19 digits use SMALLINT, INTEGER, BIGINT or DOUBLE PRECISION as base datatype depending upon number of digits and dialect. When precision is between 19 and - 34 digits DECFLOAT(34) is used for it. Actual precision is always increased to 34 digits. For - complex calculations such digits are casted (internally, in trivial way) to DECFLOAT(34) and - the result of various math (log, exp, etc.) and aggregate functions using high precision - numeric argument is DECFLOAT(34). + 38 digits 128-bit integer is used for it. Actual precision is always increased to 38 digits. + For complex calculations such digits are casted (internally) to DECFLOAT(34) and the result of + various math (log, exp, etc.) and aggregate functions using high precision numeric argument is + DECFLOAT(34). + + SET INT128 BIND - controls how are INT128 values represented in outer + world (i.e. in messages or in XSQLDA). Valid binding types are: NATIVE (use 128-bit + binary representation), CHAR/CHARACTER (use ASCII string), DOUBLE PRECISION (use + 8-byte FP representation - same as used for DOUBLE PRECISION fields) or BIGINT + with possible comma-separated SCALE clause (i.e. 'BIGINT, 3'). Various bindings + are useful if one plans to use 128-bit integers with some old client not supporting + native format. One can choose between strings (ideal precision, but poor support + for further processing), floating point values (ideal support for further processing + but poor precision) or scaled integers (good support for further processing and + required precision but range of values is very limited). When using in a tool like + generic purporse GUI client choice of CHAR binding is OK in most cases. By default + NATIVE binding is used. + The initial configuration may be specified with DPB isc_dpb_int128_bind followed + by a string with its value (case does not matter). diff --git a/extern/ttmath/ttmath.h b/extern/ttmath/ttmath.h new file mode 100644 index 0000000000..9b9e8ee6e5 --- /dev/null +++ b/extern/ttmath/ttmath.h @@ -0,0 +1,2843 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2012, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmathmathtt +#define headerfilettmathmathtt + +/*! + \file ttmath.h + \brief Mathematics functions. +*/ + +#ifdef _MSC_VER +//warning C4127: conditional expression is constant +#pragma warning( disable: 4127 ) +//warning C4702: unreachable code +#pragma warning( disable: 4702 ) +//warning C4800: forcing value to bool 'true' or 'false' (performance warning) +#pragma warning( disable: 4800 ) +#endif + +#define TTMATH_DONT_USE_WCHAR + +#include "ttmathint.h" +#include "ttmathobjects.h" + + +namespace ttmath +{ + /* + * + * functions defined here are used only with Big<> types + * + * + */ + + + /* + * + * functions for rounding + * + * + */ + + + /*! + this function skips the fraction from x + e.g 2.2 = 2 + 2.7 = 2 + -2.2 = 2 + -2.7 = 2 + */ + template + ValueType SkipFraction(const ValueType & x) + { + ValueType result( x ); + result.SkipFraction(); + + return result; + } + + + /*! + this function rounds to the nearest integer value + e.g 2.2 = 2 + 2.7 = 3 + -2.2 = -2 + -2.7 = -3 + */ + template + ValueType Round(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result( x ); + uint c = result.Round(); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + + /*! + this function returns a value representing the smallest integer + that is greater than or equal to x + + Ceil(-3.7) = -3 + Ceil(-3.1) = -3 + Ceil(-3.0) = -3 + Ceil(4.0) = 4 + Ceil(4.2) = 5 + Ceil(4.8) = 5 + */ + template + ValueType Ceil(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result(x); + uint c = 0; + + result.SkipFraction(); + + if( result != x ) + { + // x is with fraction + // if x is negative we don't have to do anything + if( !x.IsSign() ) + { + ValueType one; + one.SetOne(); + + c += result.Add(one); + } + } + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function returns a value representing the largest integer + that is less than or equal to x + + Floor(-3.6) = -4 + Floor(-3.1) = -4 + Floor(-3) = -3 + Floor(2) = 2 + Floor(2.3) = 2 + Floor(2.8) = 2 + */ + template + ValueType Floor(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result(x); + uint c = 0; + + result.SkipFraction(); + + if( result != x ) + { + // x is with fraction + // if x is positive we don't have to do anything + if( x.IsSign() ) + { + ValueType one; + one.SetOne(); + + c += result.Sub(one); + } + } + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + + /* + * + * logarithms and the exponent + * + * + */ + + + /*! + this function calculates the natural logarithm (logarithm with the base 'e') + */ + template + ValueType Ln(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result; + uint state = result.Ln(x); + + if( err ) + { + switch( state ) + { + case 0: + *err = err_ok; + break; + case 1: + *err = err_overflow; + break; + case 2: + *err = err_improper_argument; + break; + default: + *err = err_internal_error; + break; + } + } + + + return result; + } + + + /*! + this function calculates the logarithm + */ + template + ValueType Log(const ValueType & x, const ValueType & base, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) *err = err_improper_argument; + return x; + } + + if( base.IsNan() ) + { + if( err ) *err = err_improper_argument; + return base; + } + + ValueType result; + uint state = result.Log(x, base); + + if( err ) + { + switch( state ) + { + case 0: + *err = err_ok; + break; + case 1: + *err = err_overflow; + break; + case 2: + case 3: + *err = err_improper_argument; + break; + default: + *err = err_internal_error; + break; + } + } + + return result; + } + + + /*! + this function calculates the expression e^x + */ + template + ValueType Exp(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result; + uint c = result.Exp(x); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + * + * trigonometric functions + * + */ + + + /* + this namespace consists of auxiliary functions + (something like 'private' in a class) + */ + namespace auxiliaryfunctions + { + + /*! + an auxiliary function for calculating the Sine + (you don't have to call this function) + */ + template + uint PrepareSin(ValueType & x, bool & change_sign) + { + ValueType temp; + + change_sign = false; + + if( x.IsSign() ) + { + // we're using the formula 'sin(-x) = -sin(x)' + change_sign = !change_sign; + x.ChangeSign(); + } + + // we're reducing the period 2*PI + // (for big values there'll always be zero) + temp.Set2Pi(); + + if( x.Mod(temp) ) + return 1; + + + // we're setting 'x' as being in the range of <0, 0.5PI> + + temp.SetPi(); + + if( x > temp ) + { + // x is in (pi, 2*pi> + x.Sub( temp ); + change_sign = !change_sign; + } + + temp.Set05Pi(); + + if( x > temp ) + { + // x is in (0.5pi, pi> + x.Sub( temp ); + x = temp - x; + } + + return 0; + } + + + /*! + an auxiliary function for calculating the Sine + (you don't have to call this function) + + it returns Sin(x) where 'x' is from <0, PI/2> + we're calculating the Sin with using Taylor series in zero or PI/2 + (depending on which point of these two points is nearer to the 'x') + + Taylor series: + sin(x) = sin(a) + cos(a)*(x-a)/(1!) + - sin(a)*((x-a)^2)/(2!) - cos(a)*((x-a)^3)/(3!) + + sin(a)*((x-a)^4)/(4!) + ... + + when a=0 it'll be: + sin(x) = (x)/(1!) - (x^3)/(3!) + (x^5)/(5!) - (x^7)/(7!) + (x^9)/(9!) ... + + and when a=PI/2: + sin(x) = 1 - ((x-PI/2)^2)/(2!) + ((x-PI/2)^4)/(4!) - ((x-PI/2)^6)/(6!) ... + */ + template + ValueType Sin0pi05(const ValueType & x) + { + ValueType result; + ValueType numerator, denominator; + ValueType d_numerator, d_denominator; + ValueType one, temp, old_result; + + // temp = pi/4 + temp.Set05Pi(); + temp.exponent.SubOne(); + + one.SetOne(); + + if( x < temp ) + { + // we're using the Taylor series with a=0 + result = x; + numerator = x; + denominator = one; + + // d_numerator = x^2 + d_numerator = x; + d_numerator.Mul(x); + + d_denominator = 2; + } + else + { + // we're using the Taylor series with a=PI/2 + result = one; + numerator = one; + denominator = one; + + // d_numerator = (x-pi/2)^2 + ValueType pi05; + pi05.Set05Pi(); + + temp = x; + temp.Sub( pi05 ); + d_numerator = temp; + d_numerator.Mul( temp ); + + d_denominator = one; + } + + uint c = 0; + bool addition = false; + + old_result = result; + for(uint i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + { + // we're starting from a second part of the formula + c += numerator. Mul( d_numerator ); + c += denominator. Mul( d_denominator ); + c += d_denominator.Add( one ); + c += denominator. Mul( d_denominator ); + c += d_denominator.Add( one ); + temp = numerator; + c += temp.Div(denominator); + + if( c ) + // Sin is from <-1,1> and cannot make an overflow + // but the carry can be from the Taylor series + // (then we only break our calculations) + break; + + if( addition ) + result.Add( temp ); + else + result.Sub( temp ); + + + addition = !addition; + + // we're testing whether the result has changed after adding + // the next part of the Taylor formula, if not we end the loop + // (it means 'x' is zero or 'x' is PI/2 or this part of the formula + // is too small) + if( result == old_result ) + break; + + old_result = result; + } + + return result; + } + + } // namespace auxiliaryfunctions + + + + /*! + this function calculates the Sine + */ + template + ValueType Sin(ValueType x, ErrorCode * err = 0) + { + using namespace auxiliaryfunctions; + + ValueType one, result; + bool change_sign; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + if( err ) + *err = err_ok; + + if( PrepareSin( x, change_sign ) ) + { + // x is too big, we cannnot reduce the 2*PI period + // prior to version 0.8.5 the result was zero + + // result has NaN flag set by default + + if( err ) + *err = err_overflow; // maybe another error code? err_improper_argument? + + return result; // NaN is set by default + } + + result = Sin0pi05( x ); + + one.SetOne(); + + // after calculations there can be small distortions in the result + if( result > one ) + result = one; + else + if( result.IsSign() ) + // we've calculated the sin from <0, pi/2> and the result + // should be positive + result.SetZero(); + + if( change_sign ) + result.ChangeSign(); + + return result; + } + + + /*! + this function calulates the Cosine + we're using the formula cos(x) = sin(x + PI/2) + */ + template + ValueType Cos(ValueType x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType pi05; + pi05.Set05Pi(); + + uint c = x.Add( pi05 ); + + if( c ) + { + if( err ) + *err = err_overflow; + + return ValueType(); // result is undefined (NaN is set by default) + } + + return Sin(x, err); + } + + + /*! + this function calulates the Tangent + we're using the formula tan(x) = sin(x) / cos(x) + + it takes more time than calculating the Tan directly + from for example Taylor series but should be a bit preciser + because Tan receives its values from -infinity to +infinity + and when we calculate it from any series then we can make + a greater mistake than calculating 'sin/cos' + */ + template + ValueType Tan(const ValueType & x, ErrorCode * err = 0) + { + ValueType result = Cos(x, err); + + if( err && *err != err_ok ) + return result; + + if( result.IsZero() ) + { + if( err ) + *err = err_improper_argument; + + result.SetNan(); + + return result; + } + + return Sin(x, err) / result; + } + + + /*! + this function calulates the Tangent + look at the description of Tan(...) + + (the abbreviation of Tangent can be 'tg' as well) + */ + template + ValueType Tg(const ValueType & x, ErrorCode * err = 0) + { + return Tan(x, err); + } + + + /*! + this function calulates the Cotangent + we're using the formula tan(x) = cos(x) / sin(x) + + (why do we make it in this way? + look at information in Tan() function) + */ + template + ValueType Cot(const ValueType & x, ErrorCode * err = 0) + { + ValueType result = Sin(x, err); + + if( err && *err != err_ok ) + return result; + + if( result.IsZero() ) + { + if( err ) + *err = err_improper_argument; + + result.SetNan(); + + return result; + } + + return Cos(x, err) / result; + } + + + /*! + this function calulates the Cotangent + look at the description of Cot(...) + + (the abbreviation of Cotangent can be 'ctg' as well) + */ + template + ValueType Ctg(const ValueType & x, ErrorCode * err = 0) + { + return Cot(x, err); + } + + + /* + * + * inverse trigonometric functions + * + * + */ + + namespace auxiliaryfunctions + { + + /*! + an auxiliary function for calculating the Arc Sine + + we're calculating asin from the following formula: + asin(x) = x + (1*x^3)/(2*3) + (1*3*x^5)/(2*4*5) + (1*3*5*x^7)/(2*4*6*7) + ... + where abs(x) <= 1 + + we're using this formula when x is from <0, 1/2> + */ + template + ValueType ASin_0(const ValueType & x) + { + ValueType nominator, denominator, nominator_add, nominator_x, denominator_add, denominator_x; + ValueType two, result(x), x2(x); + ValueType nominator_temp, denominator_temp, old_result = result; + uint c = 0; + + x2.Mul(x); + two = 2; + + nominator.SetOne(); + denominator = two; + nominator_add = nominator; + denominator_add = denominator; + nominator_x = x; + denominator_x = 3; + + for(uint i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + { + c += nominator_x.Mul(x2); + nominator_temp = nominator_x; + c += nominator_temp.Mul(nominator); + denominator_temp = denominator; + c += denominator_temp.Mul(denominator_x); + c += nominator_temp.Div(denominator_temp); + + // if there is a carry somewhere we only break the calculating + // the result should be ok -- it's from <-pi/2, pi/2> + if( c ) + break; + + result.Add(nominator_temp); + + if( result == old_result ) + // there's no sense to calculate more + break; + + old_result = result; + + + c += nominator_add.Add(two); + c += denominator_add.Add(two); + c += nominator.Mul(nominator_add); + c += denominator.Mul(denominator_add); + c += denominator_x.Add(two); + } + + return result; + } + + + + /*! + an auxiliary function for calculating the Arc Sine + + we're calculating asin from the following formula: + asin(x) = pi/2 - sqrt(2)*sqrt(1-x) * asin_temp + asin_temp = 1 + (1*(1-x))/((2*3)*(2)) + (1*3*(1-x)^2)/((2*4*5)*(4)) + (1*3*5*(1-x)^3)/((2*4*6*7)*(8)) + ... + + where abs(x) <= 1 + + we're using this formula when x is from (1/2, 1> + */ + template + ValueType ASin_1(const ValueType & x) + { + ValueType nominator, denominator, nominator_add, nominator_x, nominator_x_add, denominator_add, denominator_x; + ValueType denominator2; + ValueType one, two, result; + ValueType nominator_temp, denominator_temp, old_result; + uint c = 0; + + two = 2; + + one.SetOne(); + nominator = one; + result = one; + old_result = result; + denominator = two; + nominator_add = nominator; + denominator_add = denominator; + nominator_x = one; + nominator_x.Sub(x); + nominator_x_add = nominator_x; + denominator_x = 3; + denominator2 = two; + + + for(uint i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + { + nominator_temp = nominator_x; + c += nominator_temp.Mul(nominator); + denominator_temp = denominator; + c += denominator_temp.Mul(denominator_x); + c += denominator_temp.Mul(denominator2); + c += nominator_temp.Div(denominator_temp); + + // if there is a carry somewhere we only break the calculating + // the result should be ok -- it's from <-pi/2, pi/2> + if( c ) + break; + + result.Add(nominator_temp); + + if( result == old_result ) + // there's no sense to calculate more + break; + + old_result = result; + + c += nominator_x.Mul(nominator_x_add); + c += nominator_add.Add(two); + c += denominator_add.Add(two); + c += nominator.Mul(nominator_add); + c += denominator.Mul(denominator_add); + c += denominator_x.Add(two); + c += denominator2.Mul(two); + } + + + nominator_x_add.exponent.AddOne(); // *2 + one.exponent.SubOne(); // =0.5 + nominator_x_add.Pow(one); // =sqrt(nominator_x_add) + result.Mul(nominator_x_add); + + one.Set05Pi(); + one.Sub(result); + + return one; + } + + + } // namespace auxiliaryfunctions + + + /*! + this function calculates the Arc Sine + x is from <-1,1> + */ + template + ValueType ASin(ValueType x, ErrorCode * err = 0) + { + using namespace auxiliaryfunctions; + + ValueType result, one; + one.SetOne(); + bool change_sign = false; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + if( x.GreaterWithoutSignThan(one) ) + { + if( err ) + *err = err_improper_argument; + + return result; // NaN is set by default + } + + if( x.IsSign() ) + { + change_sign = true; + x.Abs(); + } + + one.exponent.SubOne(); // =0.5 + + // asin(-x) = -asin(x) + if( x.GreaterWithoutSignThan(one) ) + result = ASin_1(x); + else + result = ASin_0(x); + + if( change_sign ) + result.ChangeSign(); + + if( err ) + *err = err_ok; + + return result; + } + + + /*! + this function calculates the Arc Cosine + + we're using the formula: + acos(x) = pi/2 - asin(x) + */ + template + ValueType ACos(const ValueType & x, ErrorCode * err = 0) + { + ValueType temp; + + temp.Set05Pi(); + temp.Sub(ASin(x, err)); + + return temp; + } + + + + namespace auxiliaryfunctions + { + + /*! + an auxiliary function for calculating the Arc Tangent + + arc tan (x) where x is in <0; 0.5) + (x can be in (-0.5 ; 0.5) too) + + we're using the Taylor series expanded in zero: + atan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + ... + */ + template + ValueType ATan0(const ValueType & x) + { + ValueType nominator, denominator, nominator_add, denominator_add, temp; + ValueType result, old_result; + bool adding = false; + uint c = 0; + + result = x; + old_result = result; + nominator = x; + nominator_add = x; + nominator_add.Mul(x); + + denominator.SetOne(); + denominator_add = 2; + + for(uint i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + { + c += nominator.Mul(nominator_add); + c += denominator.Add(denominator_add); + + temp = nominator; + c += temp.Div(denominator); + + if( c ) + // the result should be ok + break; + + if( adding ) + result.Add(temp); + else + result.Sub(temp); + + if( result == old_result ) + // there's no sense to calculate more + break; + + old_result = result; + adding = !adding; + } + + return result; + } + + + /*! + an auxiliary function for calculating the Arc Tangent + + where x is in <0 ; 1> + */ + template + ValueType ATan01(const ValueType & x) + { + ValueType half; + half.Set05(); + + /* + it would be better if we chose about sqrt(2)-1=0.41... instead of 0.5 here + + because as you can see below: + when x = sqrt(2)-1 + abs(x) = abs( (x-1)/(1+x) ) + so when we're calculating values around x + then they will be better converged to each other + + for example if we have x=0.4999 then during calculating ATan0(0.4999) + we have to make about 141 iterations but when we have x=0.5 + then during calculating ATan0( (x-1)/(1+x) ) we have to make + only about 89 iterations (both for Big<3,9>) + + in the future this 0.5 can be changed + */ + if( x.SmallerWithoutSignThan(half) ) + return ATan0(x); + + + /* + x>=0.5 and x<=1 + (x can be even smaller than 0.5) + + y = atac(x) + x = tan(y) + + tan(y-b) = (tan(y)-tab(b)) / (1+tan(y)*tan(b)) + y-b = atan( (tan(y)-tab(b)) / (1+tan(y)*tan(b)) ) + y = b + atan( (x-tab(b)) / (1+x*tan(b)) ) + + let b = pi/4 + tan(b) = tan(pi/4) = 1 + y = pi/4 + atan( (x-1)/(1+x) ) + + so + atac(x) = pi/4 + atan( (x-1)/(1+x) ) + when x->1 (x converges to 1) the (x-1)/(1+x) -> 0 + and we can use ATan0() function here + */ + + ValueType n(x),d(x),one,result; + + one.SetOne(); + n.Sub(one); + d.Add(one); + n.Div(d); + + result = ATan0(n); + + n.Set05Pi(); + n.exponent.SubOne(); // =pi/4 + result.Add(n); + + return result; + } + + + /*! + an auxiliary function for calculating the Arc Tangent + where x > 1 + + we're using the formula: + atan(x) = pi/2 - atan(1/x) for x>0 + */ + template + ValueType ATanGreaterThanPlusOne(const ValueType & x) + { + ValueType temp, atan; + + temp.SetOne(); + + if( temp.Div(x) ) + { + // if there was a carry here that means x is very big + // and atan(1/x) fast converged to 0 + atan.SetZero(); + } + else + atan = ATan01(temp); + + temp.Set05Pi(); + temp.Sub(atan); + + return temp; + } + + } // namespace auxiliaryfunctions + + + /*! + this function calculates the Arc Tangent + */ + template + ValueType ATan(ValueType x) + { + using namespace auxiliaryfunctions; + + ValueType one, result; + one.SetOne(); + bool change_sign = false; + + if( x.IsNan() ) + return x; + + // if x is negative we're using the formula: + // atan(-x) = -atan(x) + if( x.IsSign() ) + { + change_sign = true; + x.Abs(); + } + + if( x.GreaterWithoutSignThan(one) ) + result = ATanGreaterThanPlusOne(x); + else + result = ATan01(x); + + if( change_sign ) + result.ChangeSign(); + + return result; + } + + + /*! + this function calculates the Arc Tangent + look at the description of ATan(...) + + (the abbreviation of Arc Tangent can be 'atg' as well) + */ + template + ValueType ATg(const ValueType & x) + { + return ATan(x); + } + + + /*! + this function calculates the Arc Cotangent + + we're using the formula: + actan(x) = pi/2 - atan(x) + */ + template + ValueType ACot(const ValueType & x) + { + ValueType result; + + result.Set05Pi(); + result.Sub(ATan(x)); + + return result; + } + + + /*! + this function calculates the Arc Cotangent + look at the description of ACot(...) + + (the abbreviation of Arc Cotangent can be 'actg' as well) + */ + template + ValueType ACtg(const ValueType & x) + { + return ACot(x); + } + + + /* + * + * hyperbolic functions + * + * + */ + + + /*! + this function calculates the Hyperbolic Sine + + we're using the formula sinh(x)= ( e^x - e^(-x) ) / 2 + */ + template + ValueType Sinh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType ex, emx; + uint c = 0; + + c += ex.Exp(x); + c += emx.Exp(-x); + + c += ex.Sub(emx); + c += ex.exponent.SubOne(); + + if( err ) + *err = c ? err_overflow : err_ok; + + return ex; + } + + + /*! + this function calculates the Hyperbolic Cosine + + we're using the formula cosh(x)= ( e^x + e^(-x) ) / 2 + */ + template + ValueType Cosh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType ex, emx; + uint c = 0; + + c += ex.Exp(x); + c += emx.Exp(-x); + + c += ex.Add(emx); + c += ex.exponent.SubOne(); + + if( err ) + *err = c ? err_overflow : err_ok; + + return ex; + } + + + /*! + this function calculates the Hyperbolic Tangent + + we're using the formula tanh(x)= ( e^x - e^(-x) ) / ( e^x + e^(-x) ) + */ + template + ValueType Tanh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType ex, emx, nominator, denominator; + uint c = 0; + + c += ex.Exp(x); + c += emx.Exp(-x); + + nominator = ex; + c += nominator.Sub(emx); + denominator = ex; + c += denominator.Add(emx); + + c += nominator.Div(denominator); + + if( err ) + *err = c ? err_overflow : err_ok; + + return nominator; + } + + + /*! + this function calculates the Hyperbolic Tangent + look at the description of Tanh(...) + + (the abbreviation of Hyperbolic Tangent can be 'tgh' as well) + */ + template + ValueType Tgh(const ValueType & x, ErrorCode * err = 0) + { + return Tanh(x, err); + } + + /*! + this function calculates the Hyperbolic Cotangent + + we're using the formula coth(x)= ( e^x + e^(-x) ) / ( e^x - e^(-x) ) + */ + template + ValueType Coth(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + if( x.IsZero() ) + { + if( err ) + *err = err_improper_argument; + + return ValueType(); // NaN is set by default + } + + ValueType ex, emx, nominator, denominator; + uint c = 0; + + c += ex.Exp(x); + c += emx.Exp(-x); + + nominator = ex; + c += nominator.Add(emx); + denominator = ex; + c += denominator.Sub(emx); + + c += nominator.Div(denominator); + + if( err ) + *err = c ? err_overflow : err_ok; + + return nominator; + } + + + /*! + this function calculates the Hyperbolic Cotangent + look at the description of Coth(...) + + (the abbreviation of Hyperbolic Cotangent can be 'ctgh' as well) + */ + template + ValueType Ctgh(const ValueType & x, ErrorCode * err = 0) + { + return Coth(x, err); + } + + + /* + * + * inverse hyperbolic functions + * + * + */ + + + /*! + inverse hyperbolic sine + + asinh(x) = ln( x + sqrt(x^2 + 1) ) + */ + template + ValueType ASinh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType xx(x), one, result; + uint c = 0; + one.SetOne(); + + c += xx.Mul(x); + c += xx.Add(one); + one.exponent.SubOne(); // one=0.5 + // xx is >= 1 + c += xx.PowFrac(one); // xx=sqrt(xx) + c += xx.Add(x); + c += result.Ln(xx); // xx > 0 + + // here can only be a carry + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + inverse hyperbolic cosine + + acosh(x) = ln( x + sqrt(x^2 - 1) ) x in <1, infinity) + */ + template + ValueType ACosh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType xx(x), one, result; + uint c = 0; + one.SetOne(); + + if( x < one ) + { + if( err ) + *err = err_improper_argument; + + return result; // NaN is set by default + } + + c += xx.Mul(x); + c += xx.Sub(one); + // xx is >= 0 + // we can't call a PowFrac when the 'x' is zero + // if x is 0 the sqrt(0) is 0 + if( !xx.IsZero() ) + { + one.exponent.SubOne(); // one=0.5 + c += xx.PowFrac(one); // xx=sqrt(xx) + } + c += xx.Add(x); + c += result.Ln(xx); // xx >= 1 + + // here can only be a carry + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + inverse hyperbolic tangent + + atanh(x) = 0.5 * ln( (1+x) / (1-x) ) x in (-1, 1) + */ + template + ValueType ATanh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType nominator(x), denominator, one, result; + uint c = 0; + one.SetOne(); + + if( !x.SmallerWithoutSignThan(one) ) + { + if( err ) + *err = err_improper_argument; + + return result; // NaN is set by default + } + + c += nominator.Add(one); + denominator = one; + c += denominator.Sub(x); + c += nominator.Div(denominator); + c += result.Ln(nominator); + c += result.exponent.SubOne(); + + // here can only be a carry + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + inverse hyperbolic tantent + */ + template + ValueType ATgh(const ValueType & x, ErrorCode * err = 0) + { + return ATanh(x, err); + } + + + /*! + inverse hyperbolic cotangent + + acoth(x) = 0.5 * ln( (x+1) / (x-1) ) x in (-infinity, -1) or (1, infinity) + */ + template + ValueType ACoth(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType nominator(x), denominator(x), one, result; + uint c = 0; + one.SetOne(); + + if( !x.GreaterWithoutSignThan(one) ) + { + if( err ) + *err = err_improper_argument; + + return result; // NaN is set by default + } + + c += nominator.Add(one); + c += denominator.Sub(one); + c += nominator.Div(denominator); + c += result.Ln(nominator); + c += result.exponent.SubOne(); + + // here can only be a carry + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + inverse hyperbolic cotantent + */ + template + ValueType ACtgh(const ValueType & x, ErrorCode * err = 0) + { + return ACoth(x, err); + } + + + + + + /* + * + * functions for converting between degrees, radians and gradians + * + * + */ + + + /*! + this function converts degrees to radians + + it returns: x * pi / 180 + */ + template + ValueType DegToRad(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, temp; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = x; + + // it is better to make division first and then multiplication + // the result is more accurate especially when x is: 90,180,270 or 360 + temp = 180; + c += result.Div(temp); + + temp.SetPi(); + c += result.Mul(temp); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts radians to degrees + + it returns: x * 180 / pi + */ + template + ValueType RadToDeg(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, delimiter; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = 180; + c += result.Mul(x); + + delimiter.SetPi(); + c += result.Div(delimiter); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts degrees in the long format into one value + + long format: (degrees, minutes, seconds) + minutes and seconds must be greater than or equal zero + + result: + if d>=0 : result= d + ((s/60)+m)/60 + if d<0 : result= d - ((s/60)+m)/60 + + ((s/60)+m)/60 = (s+60*m)/3600 (second version is faster because + there's only one division) + + for example: + DegToDeg(10, 30, 0) = 10.5 + DegToDeg(10, 24, 35.6)=10.4098(8) + */ + template + ValueType DegToDeg( const ValueType & d, const ValueType & m, const ValueType & s, + ErrorCode * err = 0) + { + ValueType delimiter, multipler; + uint c = 0; + + if( d.IsNan() || m.IsNan() || s.IsNan() || m.IsSign() || s.IsSign() ) + { + if( err ) + *err = err_improper_argument; + + delimiter.SetZeroNan(); // not needed, only to get rid of GCC warning about an uninitialized variable + + return delimiter; + } + + multipler = 60; + delimiter = 3600; + + c += multipler.Mul(m); + c += multipler.Add(s); + c += multipler.Div(delimiter); + + if( d.IsSign() ) + multipler.ChangeSign(); + + c += multipler.Add(d); + + if( err ) + *err = c ? err_overflow : err_ok; + + return multipler; + } + + + /*! + this function converts degrees in the long format to radians + */ + template + ValueType DegToRad( const ValueType & d, const ValueType & m, const ValueType & s, + ErrorCode * err = 0) + { + ValueType temp_deg = DegToDeg(d,m,s,err); + + if( err && *err!=err_ok ) + return temp_deg; + + return DegToRad(temp_deg, err); + } + + + /*! + this function converts gradians to radians + + it returns: x * pi / 200 + */ + template + ValueType GradToRad(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, temp; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = x; + + // it is better to make division first and then multiplication + // the result is more accurate especially when x is: 100,200,300 or 400 + temp = 200; + c += result.Div(temp); + + temp.SetPi(); + c += result.Mul(temp); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts radians to gradians + + it returns: x * 200 / pi + */ + template + ValueType RadToGrad(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, delimiter; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = 200; + c += result.Mul(x); + + delimiter.SetPi(); + c += result.Div(delimiter); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts degrees to gradians + + it returns: x * 200 / 180 + */ + template + ValueType DegToGrad(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, temp; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = x; + + temp = 200; + c += result.Mul(temp); + + temp = 180; + c += result.Div(temp); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts degrees in the long format to gradians + */ + template + ValueType DegToGrad( const ValueType & d, const ValueType & m, const ValueType & s, + ErrorCode * err = 0) + { + ValueType temp_deg = DegToDeg(d,m,s,err); + + if( err && *err!=err_ok ) + return temp_deg; + + return DegToGrad(temp_deg, err); + } + + + /*! + this function converts degrees to gradians + + it returns: x * 180 / 200 + */ + template + ValueType GradToDeg(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, temp; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = x; + + temp = 180; + c += result.Mul(temp); + + temp = 200; + c += result.Div(temp); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + + + /* + * + * another functions + * + * + */ + + + /*! + this function calculates the square root + + Sqrt(9) = 3 + */ + template + ValueType Sqrt(ValueType x, ErrorCode * err = 0) + { + if( x.IsNan() || x.IsSign() ) + { + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return x; + } + + uint c = x.Sqrt(); + + if( err ) + *err = c ? err_overflow : err_ok; + + return x; + } + + + + namespace auxiliaryfunctions + { + + template + bool RootCheckIndexSign(ValueType & x, const ValueType & index, ErrorCode * err) + { + if( index.IsSign() ) + { + // index cannot be negative + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return true; + } + + return false; + } + + + template + bool RootCheckIndexZero(ValueType & x, const ValueType & index, ErrorCode * err) + { + if( index.IsZero() ) + { + if( x.IsZero() ) + { + // there isn't root(0;0) - we assume it's not defined + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return true; + } + + // root(x;0) is 1 (if x!=0) + x.SetOne(); + + if( err ) + *err = err_ok; + + return true; + } + + return false; + } + + + template + bool RootCheckIndexOne(const ValueType & index, ErrorCode * err) + { + ValueType one; + one.SetOne(); + + if( index == one ) + { + //root(x;1) is x + // we do it because if we used the PowFrac function + // we would lose the precision + if( err ) + *err = err_ok; + + return true; + } + + return false; + } + + + template + bool RootCheckIndexTwo(ValueType & x, const ValueType & index, ErrorCode * err) + { + if( index == 2 ) + { + x = Sqrt(x, err); + + return true; + } + + return false; + } + + + template + bool RootCheckIndexFrac(ValueType & x, const ValueType & index, ErrorCode * err) + { + if( !index.IsInteger() ) + { + // index must be integer + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return true; + } + + return false; + } + + + template + bool RootCheckXZero(ValueType & x, ErrorCode * err) + { + if( x.IsZero() ) + { + // root(0;index) is zero (if index!=0) + // RootCheckIndexZero() must be called beforehand + x.SetZero(); + + if( err ) + *err = err_ok; + + return true; + } + + return false; + } + + + template + bool RootCheckIndex(ValueType & x, const ValueType & index, ErrorCode * err, bool * change_sign) + { + *change_sign = false; + + if( index.Mod2() ) + { + // index is odd (1,3,5...) + if( x.IsSign() ) + { + *change_sign = true; + x.Abs(); + } + } + else + { + // index is even + // x cannot be negative + if( x.IsSign() ) + { + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return true; + } + } + + return false; + } + + + template + uint RootCorrectInteger(ValueType & old_x, ValueType & x, const ValueType & index) + { + if( !old_x.IsInteger() || x.IsInteger() || !index.exponent.IsSign() ) + return 0; + + // old_x is integer, + // x is not integer, + // index is relatively small (index.exponent<0 or index.exponent<=0) + // (because we're using a special powering algorithm Big::PowUInt()) + + uint c = 0; + + ValueType temp(x); + c += temp.Round(); + + ValueType temp_round(temp); + c += temp.PowUInt(index); + + if( temp == old_x ) + x = temp_round; + + return (c==0)? 0 : 1; + } + + + + } // namespace auxiliaryfunctions + + + + /*! + indexth Root of x + index must be integer and not negative <0;1;2;3....) + + if index==0 the result is one + if x==0 the result is zero and we assume root(0;0) is not defined + + if index is even (2;4;6...) the result is x^(1/index) and x>0 + if index is odd (1;2;3;...) the result is either + -(abs(x)^(1/index)) if x<0 or + x^(1/index)) if x>0 + + (for index==1 the result is equal x) + */ + template + ValueType Root(ValueType x, const ValueType & index, ErrorCode * err = 0) + { + using namespace auxiliaryfunctions; + + if( x.IsNan() || index.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return x; + } + + if( RootCheckIndexSign(x, index, err) ) return x; + if( RootCheckIndexZero(x, index, err) ) return x; + if( RootCheckIndexOne ( index, err) ) return x; + if( RootCheckIndexTwo (x, index, err) ) return x; + if( RootCheckIndexFrac(x, index, err) ) return x; + if( RootCheckXZero (x, err) ) return x; + + // index integer and index!=0 + // x!=0 + + ValueType old_x(x); + bool change_sign; + + if( RootCheckIndex(x, index, err, &change_sign ) ) return x; + + ValueType temp; + uint c = 0; + + // we're using the formula: root(x ; n) = exp( ln(x) / n ) + c += temp.Ln(x); + c += temp.Div(index); + c += x.Exp(temp); + + if( change_sign ) + { + // x is different from zero + x.SetSign(); + } + + c += RootCorrectInteger(old_x, x, index); + + if( err ) + *err = c ? err_overflow : err_ok; + + return x; + } + + + + /*! + absolute value of x + e.g. -2 = 2 + 2 = 2 + */ + template + ValueType Abs(const ValueType & x) + { + ValueType result( x ); + result.Abs(); + + return result; + } + + + /*! + it returns the sign of the value + e.g. -2 = -1 + 0 = 0 + 10 = 1 + */ + template + ValueType Sgn(ValueType x) + { + x.Sgn(); + + return x; + } + + + /*! + the remainder from a division + + e.g. + mod( 12.6 ; 3) = 0.6 because 12.6 = 3*4 + 0.6 + mod(-12.6 ; 3) = -0.6 bacause -12.6 = 3*(-4) + (-0.6) + mod( 12.6 ; -3) = 0.6 + mod(-12.6 ; -3) = -0.6 + */ + template + ValueType Mod(ValueType a, const ValueType & b, ErrorCode * err = 0) + { + if( a.IsNan() || b.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + a.SetNan(); + + return a; + } + + uint c = a.Mod(b); + + if( err ) + *err = c ? err_overflow : err_ok; + + return a; + } + + + + namespace auxiliaryfunctions + { + + /*! + this function is used to store factorials in a given container + 'more' means how many values should be added at the end + + e.g. + std::vector fact; + SetFactorialSequence(fact, 3); + // now the container has three values: 1 1 2 + + SetFactorialSequence(fact, 2); + // now the container has five values: 1 1 2 6 24 + */ + template + void SetFactorialSequence(std::vector & fact, uint more = 20) + { + if( more == 0 ) + more = 1; + + uint start = static_cast(fact.size()); + fact.resize(fact.size() + more); + + if( start == 0 ) + { + fact[0] = 1; + ++start; + } + + for(uint i=start ; i + ValueType SetBernoulliNumbersSum(CGamma & cgamma, const ValueType & n_, uint m, + const volatile StopCalculating * stop = 0) + { + ValueType k_, temp, temp2, temp3, sum; + + sum.SetZero(); + + for(uint k=0 ; kWasStopSignal() ) + return ValueType(); // NaN + + if( k>1 && (k & 1) == 1 ) // for that k the Bernoulli number is zero + continue; + + k_ = k; + + temp = n_; // n_ is equal 2 + temp.Pow(k_); + // temp = 2^k + + temp2 = cgamma.fact[m]; + temp3 = cgamma.fact[k]; + temp3.Mul(cgamma.fact[m-k]); + temp2.Div(temp3); + // temp2 = (m k) = m! / ( k! * (m-k)! ) + + temp.Mul(temp2); + temp.Mul(cgamma.bern[k]); + + sum.Add(temp); + // sum += 2^k * (m k) * B(k) + + if( sum.IsNan() ) + break; + } + + return sum; + } + + + /*! + an auxiliary function used to calculate Bernoulli numbers + start is >= 2 + + we use the recurrence formula: + B(m) = 1 / (2*(1 - 2^m)) * sum(m) + where sum(m) is calculated by SetBernoulliNumbersSum() + */ + template + bool SetBernoulliNumbersMore(CGamma & cgamma, uint start, const volatile StopCalculating * stop = 0) + { + ValueType denominator, temp, temp2, temp3, m_, sum, sum2, n_, k_; + + const uint n = 2; + n_ = n; + + // start is >= 2 + for(uint m=start ; mWasStopSignal() ) + { + cgamma.bern.resize(m); // valid numbers are in [0, m-1] + return false; + } + + cgamma.bern[m].Div(denominator); + } + } + + return true; + } + + + /*! + this function is used to calculate Bernoulli numbers, + returns false if there was a stop signal, + 'more' means how many values should be added at the end + + e.g. + typedef Big<1,2> MyBig; + CGamma cgamma; + SetBernoulliNumbers(cgamma, 3); + // now we have three first Bernoulli numbers: 1 -0.5 0.16667 + + SetBernoulliNumbers(cgamma, 4); + // now we have 7 Bernoulli numbers: 1 -0.5 0.16667 0 -0.0333 0 0.0238 + */ + template + bool SetBernoulliNumbers(CGamma & cgamma, uint more = 20, const volatile StopCalculating * stop = 0) + { + if( more == 0 ) + more = 1; + + uint start = static_cast(cgamma.bern.size()); + cgamma.bern.resize(cgamma.bern.size() + more); + + if( start == 0 ) + { + cgamma.bern[0].SetOne(); + ++start; + } + + if( cgamma.bern.size() == 1 ) + return true; + + if( start == 1 ) + { + cgamma.bern[1].Set05(); + cgamma.bern[1].ChangeSign(); + ++start; + } + + // we should have sufficient factorials in cgamma.fact + if( cgamma.fact.size() < cgamma.bern.size() ) + SetFactorialSequence(cgamma.fact, static_cast(cgamma.bern.size() - cgamma.fact.size())); + + + return SetBernoulliNumbersMore(cgamma, start, stop); + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + we calculate a sum: + sum(n) = sum_{m=2} { B(m) / ( (m^2 - m) * n^(m-1) ) } = 1/(12*n) - 1/(360*n^3) + 1/(1260*n^5) + ... + B(m) means a mth Bernoulli number + the sum starts from m=2, we calculate as long as the value will not change after adding a next part + */ + template + ValueType GammaFactorialHighSum(const ValueType & n, CGamma & cgamma, ErrorCode & err, + const volatile StopCalculating * stop) + { + ValueType temp, temp2, denominator, sum, oldsum; + + sum.SetZero(); + + for(uint m=2 ; mWasStopSignal() ) + { + err = err_interrupt; + return ValueType(); // NaN + } + + temp = (m-1); + denominator = n; + denominator.Pow(temp); + // denominator = n ^ (m-1) + + temp = m; + temp2 = temp; + temp.Mul(temp2); + temp.Sub(temp2); + // temp = m^2 - m + + denominator.Mul(temp); + // denominator = (m^2 - m) * n ^ (m-1) + + if( m >= cgamma.bern.size() ) + { + if( !SetBernoulliNumbers(cgamma, m - cgamma.bern.size() + 1 + 3, stop) ) // 3 more than needed + { + // there was the stop signal + err = err_interrupt; + return ValueType(); // NaN + } + } + + temp = cgamma.bern[m]; + temp.Div(denominator); + + oldsum = sum; + sum.Add(temp); + + if( sum.IsNan() || oldsum==sum ) + break; + } + + return sum; + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + we calculate a helper function GammaFactorialHigh() by using Stirling's series: + n! = (n/e)^n * sqrt(2*pi*n) * exp( sum(n) ) + where n is a real number (not only an integer) and is sufficient large (greater than TTMATH_GAMMA_BOUNDARY) + and sum(n) is calculated by GammaFactorialHighSum() + */ + template + ValueType GammaFactorialHigh(const ValueType & n, CGamma & cgamma, ErrorCode & err, + const volatile StopCalculating * stop) + { + ValueType temp, temp2, temp3, denominator, sum; + + temp.Set2Pi(); + temp.Mul(n); + temp2 = Sqrt(temp); + // temp2 = sqrt(2*pi*n) + + temp = n; + temp3.SetE(); + temp.Div(temp3); + temp.Pow(n); + // temp = (n/e)^n + + sum = GammaFactorialHighSum(n, cgamma, err, stop); + temp3.Exp(sum); + // temp3 = exp(sum) + + temp.Mul(temp2); + temp.Mul(temp3); + + return temp; + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + Gamma(x) = GammaFactorialHigh(x-1) + */ + template + ValueType GammaPlusHigh(ValueType n, CGamma & cgamma, ErrorCode & err, const volatile StopCalculating * stop) + { + ValueType one; + + one.SetOne(); + n.Sub(one); + + return GammaFactorialHigh(n, cgamma, err, stop); + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + we use this function when n is integer and a small value (from 0 to TTMATH_GAMMA_BOUNDARY] + we use the formula: + gamma(n) = (n-1)! = 1 * 2 * 3 * ... * (n-1) + */ + template + ValueType GammaPlusLowIntegerInt(uint n, CGamma & cgamma) + { + TTMATH_ASSERT( n > 0 ) + + if( n - 1 < static_cast(cgamma.fact.size()) ) + return cgamma.fact[n - 1]; + + ValueType res; + uint start = 2; + + if( cgamma.fact.size() < 2 ) + { + res.SetOne(); + } + else + { + start = static_cast(cgamma.fact.size()); + res = cgamma.fact[start-1]; + } + + for(uint i=start ; i + ValueType GammaPlusLowInteger(const ValueType & n, CGamma & cgamma) + { + sint n_; + + n.ToInt(n_); + + return GammaPlusLowIntegerInt(n_, cgamma); + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + we use this function when n is a small value (from 0 to TTMATH_GAMMA_BOUNDARY] + we use a recurrence formula: + gamma(z+1) = z * gamma(z) + then: gamma(z) = gamma(z+1) / z + + e.g. + gamma(3.89) = gamma(2001.89) / ( 3.89 * 4.89 * 5.89 * ... * 1999.89 * 2000.89 ) + */ + template + ValueType GammaPlusLow(ValueType n, CGamma & cgamma, ErrorCode & err, const volatile StopCalculating * stop) + { + ValueType one, denominator, temp, boundary; + + if( n.IsInteger() ) + return GammaPlusLowInteger(n, cgamma); + + one.SetOne(); + denominator = n; + boundary = TTMATH_GAMMA_BOUNDARY; + + while( n < boundary ) + { + n.Add(one); + denominator.Mul(n); + } + + n.Add(one); + + // now n is sufficient big + temp = GammaPlusHigh(n, cgamma, err, stop); + temp.Div(denominator); + + return temp; + } + + + /*! + an auxiliary function used to calculate the Gamma() function + */ + template + ValueType GammaPlus(const ValueType & n, CGamma & cgamma, ErrorCode & err, const volatile StopCalculating * stop) + { + if( n > TTMATH_GAMMA_BOUNDARY ) + return GammaPlusHigh(n, cgamma, err, stop); + + return GammaPlusLow(n, cgamma, err, stop); + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + this function is used when n is negative + we use the reflection formula: + gamma(1-z) * gamma(z) = pi / sin(pi*z) + then: gamma(z) = pi / (sin(pi*z) * gamma(1-z)) + + */ + template + ValueType GammaMinus(const ValueType & n, CGamma & cgamma, ErrorCode & err, const volatile StopCalculating * stop) + { + ValueType pi, denominator, temp, temp2; + + if( n.IsInteger() ) + { + // gamma function is not defined when n is negative and integer + err = err_improper_argument; + return temp; // NaN + } + + pi.SetPi(); + + temp = pi; + temp.Mul(n); + temp2 = Sin(temp); + // temp2 = sin(pi * n) + + temp.SetOne(); + temp.Sub(n); + temp = GammaPlus(temp, cgamma, err, stop); + // temp = gamma(1 - n) + + temp.Mul(temp2); + pi.Div(temp); + + return pi; + } + + } // namespace auxiliaryfunctions + + + + /*! + this function calculates the Gamma function + + it's multithread safe, you should create a CGamma<> object and use it whenever you call the Gamma() + e.g. + typedef Big<1,2> MyBig; + MyBig x=234, y=345.53; + CGamma cgamma; + std::cout << Gamma(x, cgamma) << std::endl; + std::cout << Gamma(y, cgamma) << std::endl; + in the CGamma<> object the function stores some coefficients (factorials, Bernoulli numbers), + and they will be reused in next calls to the function + + each thread should have its own CGamma<> object, and you can use these objects with Factorial() function too + */ + template + ValueType Gamma(const ValueType & n, CGamma & cgamma, ErrorCode * err = 0, + const volatile StopCalculating * stop = 0) + { + using namespace auxiliaryfunctions; + + ValueType result; + ErrorCode err_tmp; + + if( n.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return n; + } + + if( cgamma.history.Get(n, result, err_tmp) ) + { + if( err ) + *err = err_tmp; + + return result; + } + + err_tmp = err_ok; + + if( n.IsSign() ) + { + result = GammaMinus(n, cgamma, err_tmp, stop); + } + else + if( n.IsZero() ) + { + err_tmp = err_improper_argument; + result.SetNan(); + } + else + { + result = GammaPlus(n, cgamma, err_tmp, stop); + } + + if( result.IsNan() && err_tmp==err_ok ) + err_tmp = err_overflow; + + if( err ) + *err = err_tmp; + + if( stop && !stop->WasStopSignal() ) + cgamma.history.Add(n, result, err_tmp); + + return result; + } + + + /*! + this function calculates the Gamma function + + note: this function should be used only in a single-thread environment + */ + template + ValueType Gamma(const ValueType & n, ErrorCode * err = 0) + { + // warning: this static object is not thread safe + static CGamma cgamma; + + return Gamma(n, cgamma, err); + } + + + + namespace auxiliaryfunctions + { + + /*! + an auxiliary function for calculating the factorial function + + we use the formula: + x! = gamma(x+1) + */ + template + ValueType Factorial2(ValueType x, + CGamma * cgamma = 0, + ErrorCode * err = 0, + const volatile StopCalculating * stop = 0) + { + ValueType result, one; + + if( x.IsNan() || x.IsSign() || !x.IsInteger() ) + { + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return x; + } + + one.SetOne(); + x.Add(one); + + if( cgamma ) + return Gamma(x, *cgamma, err, stop); + + return Gamma(x, err); + } + + } // namespace auxiliaryfunctions + + + + /*! + the factorial from given 'x' + e.g. + Factorial(4) = 4! = 1*2*3*4 + + it's multithread safe, you should create a CGamma<> object and use it whenever you call the Factorial() + e.g. + typedef Big<1,2> MyBig; + MyBig x=234, y=54345; + CGamma cgamma; + std::cout << Factorial(x, cgamma) << std::endl; + std::cout << Factorial(y, cgamma) << std::endl; + in the CGamma<> object the function stores some coefficients (factorials, Bernoulli numbers), + and they will be reused in next calls to the function + + each thread should have its own CGamma<> object, and you can use these objects with Gamma() function too + */ + template + ValueType Factorial(const ValueType & x, CGamma & cgamma, ErrorCode * err = 0, + const volatile StopCalculating * stop = 0) + { + return auxiliaryfunctions::Factorial2(x, &cgamma, err, stop); + } + + + /*! + the factorial from given 'x' + e.g. + Factorial(4) = 4! = 1*2*3*4 + + note: this function should be used only in a single-thread environment + */ + template + ValueType Factorial(const ValueType & x, ErrorCode * err = 0) + { + return auxiliaryfunctions::Factorial2(x, (CGamma*)0, err, 0); + } + + + /*! + this method prepares some coefficients: factorials and Bernoulli numbers + stored in 'fact' and 'bern' objects + + we're defining the method here because we're using Gamma() function which + is not available in ttmathobjects.h + + read the doc info in ttmathobjects.h file where CGamma<> struct is declared + */ + template + void CGamma::InitAll() + { + ValueType x = TTMATH_GAMMA_BOUNDARY + 1; + + // history.Remove(x) removes only one object + // we must be sure that there are not others objects with the key 'x' + while( history.Remove(x) ) + { + } + + // the simplest way to initialize is to call the Gamma function with (TTMATH_GAMMA_BOUNDARY + 1) + // when x is larger then fewer coefficients we need + Gamma(x, *this); + } + + + +} // namespace + + +#ifdef _MSC_VER +//warning C4127: conditional expression is constant +#pragma warning( default: 4127 ) +//warning C4702: unreachable code +#pragma warning( default: 4702 ) +//warning C4800: forcing value to bool 'true' or 'false' (performance warning) +#pragma warning( default: 4800 ) +#endif + +#endif diff --git a/extern/ttmath/ttmathint.h b/extern/ttmath/ttmathint.h new file mode 100644 index 0000000000..8ad0189f93 --- /dev/null +++ b/extern/ttmath/ttmathint.h @@ -0,0 +1,1917 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2011, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmathint +#define headerfilettmathint + +/*! + \file ttmathint.h + \brief template class Int +*/ + +#include "ttmathuint.h" + +namespace ttmath +{ + + +/*! + \brief Int implements a big integer value with a sign + + value_size - how many bytes specify our value + on 32bit platforms: value_size=1 -> 4 bytes -> 32 bits + on 64bit platforms: value_size=1 -> 8 bytes -> 64 bits + value_size = 1,2,3,4,5,6.... +*/ +template +class Int : public UInt +{ +public: + + /*! + this method sets the max value which this class can hold + (all bits will be one besides the last one) + */ + void SetMax() + { + UInt::SetMax(); + UInt::table[value_size-1] = ~ TTMATH_UINT_HIGHEST_BIT; + } + + + /*! + this method sets the min value which this class can hold + (all bits will be zero besides the last one which is one) + */ + void SetMin() + { + UInt::SetZero(); + UInt::table[value_size-1] = TTMATH_UINT_HIGHEST_BIT; + } + + + /*! + this method sets -1 as the value + (-1 is equal the max value in an unsigned type) + */ + void SetSignOne() + { + UInt::SetMax(); + } + + + /*! + we change the sign of the value + + if it isn't possible to change the sign this method returns 1 + else return 0 and changing the sign + */ + uint ChangeSign() + { + /* + if the value is equal that one which has been returned from SetMin + (only the highest bit is set) that means we can't change sign + because the value is too big (bigger about one) + + e.g. when value_size = 1 and value is -2147483648 we can't change it to the + 2147483648 because the max value which can be held is 2147483647 + + we don't change the value and we're using this fact somewhere in some methods + (if we look on our value without the sign we get the correct value + eg. -2147483648 in Int<1> will be 2147483648 on the UInt<1> type) + */ + if( UInt::IsOnlyTheHighestBitSet() ) + return 1; + + UInt temp(*this); + UInt::SetZero(); + UInt::Sub(temp); + + return 0; + } + + + + /*! + this method sets the sign + + e.g. 1 -> -1 + -2 -> -2 + + from a positive value we make a negative value, + if the value is negative we do nothing + */ + void SetSign() + { + if( IsSign() ) + return; + + ChangeSign(); + } + + + + /*! + this method returns true if there's the sign + + (the highest bit will be converted to the bool) + */ + bool IsSign() const + { + return UInt::IsTheHighestBitSet(); + } + + + + /*! + it sets an absolute value + + it can return carry (1) (look on ChangeSign() for details) + */ + uint Abs() + { + if( !IsSign() ) + return 0; + + return ChangeSign(); + } + + + + + /*! + * + * basic mathematic functions + * + */ + +private: + + uint CorrectCarryAfterAdding(bool p1_is_sign, bool p2_is_sign) + { + if( !p1_is_sign && !p2_is_sign ) + { + if( UInt::IsTheHighestBitSet() ) + return 1; + } + + if( p1_is_sign && p2_is_sign ) + { + if( ! UInt::IsTheHighestBitSet() ) + return 1; + } + + return 0; + } + + +public: + + /*! + this method adds two value with a sign and returns a carry + + we're using methods from the base class because values are stored with U2 + we must only make the carry correction + + this = p1(=this) + p2 + + when p1>=0 i p2>=0 carry is set when the highest bit of value is set + when p1<0 i p2<0 carry is set when the highest bit of value is clear + when p1>=0 i p2<0 carry will never be set + when p1<0 i p2>=0 carry will never be set + */ + uint Add(const Int & ss2) + { + bool p1_is_sign = IsSign(); + bool p2_is_sign = ss2.IsSign(); + + UInt::Add(ss2); + + return CorrectCarryAfterAdding(p1_is_sign, p2_is_sign); + } + + + /*! + this method adds one *unsigned* word (at a specific position) + and returns a carry (if it was) + + look at a description in UInt<>::AddInt(...) + */ + uint AddInt(uint value, uint index = 0) + { + bool p1_is_sign = IsSign(); + + UInt::AddInt(value, index); + + return CorrectCarryAfterAdding(p1_is_sign, false); + } + + + /*! + this method adds two *unsigned* words to the existing value + and these words begin on the 'index' position + + index should be equal or smaller than value_size-2 (index <= value_size-2) + x1 - lower word, x2 - higher word + + look at a description in UInt<>::AddTwoInts(...) + */ + uint AddTwoInts(uint x2, uint x1, uint index) + { + bool p1_is_sign = IsSign(); + + UInt::AddTwoInts(x2, x1, index); + + return CorrectCarryAfterAdding(p1_is_sign, false); + } + +private: + + uint CorrectCarryAfterSubtracting(bool p1_is_sign, bool p2_is_sign) + { + if( !p1_is_sign && p2_is_sign ) + { + if( UInt::IsTheHighestBitSet() ) + return 1; + } + + if( p1_is_sign && !p2_is_sign ) + { + if( ! UInt::IsTheHighestBitSet() ) + return 1; + } + + return 0; + } + +public: + + /*! + this method subtracts two values with a sign + + we don't use the previous Add because the method ChangeSign can + sometimes return carry + + this = p1(=this) - p2 + + when p1>=0 i p2>=0 carry will never be set + when p1<0 i p2<0 carry will never be set + when p1>=0 i p2<0 carry is set when the highest bit of value is set + when p1<0 i p2>=0 carry is set when the highest bit of value is clear + */ + uint Sub(const Int & ss2) + { + bool p1_is_sign = IsSign(); + bool p2_is_sign = ss2.IsSign(); + + UInt::Sub(ss2); + + return CorrectCarryAfterSubtracting(p1_is_sign, p2_is_sign); + } + + + /*! + this method subtracts one *unsigned* word (at a specific position) + and returns a carry (if it was) + */ + uint SubInt(uint value, uint index = 0) + { + bool p1_is_sign = IsSign(); + + UInt::SubInt(value, index); + + return CorrectCarryAfterSubtracting(p1_is_sign, false); + } + + + /*! + this method adds one to the value and returns carry + */ + uint AddOne() + { + bool p1_is_sign = IsSign(); + + UInt::AddOne(); + + return CorrectCarryAfterAdding(p1_is_sign, false); + } + + + /*! + this method subtracts one from the value and returns carry + */ + uint SubOne() + { + bool p1_is_sign = IsSign(); + + UInt::SubOne(); + + return CorrectCarryAfterSubtracting(p1_is_sign, false); + } + + +private: + + + uint CheckMinCarry(bool ss1_is_sign, bool ss2_is_sign) + { + /* + we have to examine the sign of the result now + but if the result is with the sign then: + 1. if the signs were the same that means the result is too big + (the result must be without a sign) + 2. if the signs were different that means if the result + is different from that one which has been returned from SetMin() + that is carry (result too big) but if the result is equal SetMin() + there'll be ok (and the next SetSign will has no effect because + the value is actually negative -- look at description of that case + in ChangeSign()) + */ + if( IsSign() ) + { + if( ss1_is_sign != ss2_is_sign ) + { + /* + there can be one case where signs are different and + the result will be equal the value from SetMin() (only the highest bit is set) + (this situation is ok) + */ + if( !UInt::IsOnlyTheHighestBitSet() ) + return 1; + } + else + { + // signs were the same + return 1; + } + } + + return 0; + } + + +public: + + + /*! + multiplication: this = this * ss2 + + it can return a carry + */ + uint MulInt(sint ss2) + { + bool ss1_is_sign, ss2_is_sign; + uint c; + + ss1_is_sign = IsSign(); + + /* + we don't have to check the carry from Abs (values will be correct + because next we're using the method MulInt from the base class UInt + which is without a sign) + */ + Abs(); + + if( ss2 < 0 ) + { + ss2 = -ss2; + ss2_is_sign = true; + } + else + { + ss2_is_sign = false; + } + + c = UInt::MulInt((uint)ss2); + c += CheckMinCarry(ss1_is_sign, ss2_is_sign); + + if( ss1_is_sign != ss2_is_sign ) + SetSign(); + + return c; + } + + + + /*! + multiplication this = this * ss2 + + it returns carry if the result is too big + (we're using the method from the base class but we have to make + one correction in account of signs) + */ + uint Mul(Int ss2) + { + bool ss1_is_sign, ss2_is_sign; + uint c; + + ss1_is_sign = IsSign(); + ss2_is_sign = ss2.IsSign(); + + /* + we don't have to check the carry from Abs (values will be correct + because next we're using the method Mul from the base class UInt + which is without a sign) + */ + Abs(); + ss2.Abs(); + + c = UInt::Mul(ss2); + c += CheckMinCarry(ss1_is_sign, ss2_is_sign); + + if( ss1_is_sign != ss2_is_sign ) + SetSign(); + + return c; + } + + + /*! + division this = this / ss2 + returned values: + 0 - ok + 1 - division by zero + + for example: (result means 'this') + 20 / 3 --> result: 6 remainder: 2 + -20 / 3 --> result: -6 remainder: -2 + 20 / -3 --> result: -6 remainder: 2 + -20 / -3 --> result: 6 remainder: -2 + + in other words: this(old) = ss2 * this(new)(result) + remainder + */ + uint Div(Int ss2, Int * remainder = 0) + { + bool ss1_is_sign, ss2_is_sign; + + ss1_is_sign = IsSign(); + ss2_is_sign = ss2.IsSign(); + + /* + we don't have to test the carry from Abs as well as in Mul + */ + Abs(); + ss2.Abs(); + + uint c = UInt::Div(ss2, remainder); + + if( ss1_is_sign != ss2_is_sign ) + SetSign(); + + if( ss1_is_sign && remainder ) + remainder->SetSign(); + + return c; + } + + uint Div(const Int & ss2, Int & remainder) + { + return Div(ss2, &remainder); + } + + + /*! + division this = this / ss2 (ss2 is int) + returned values: + 0 - ok + 1 - division by zero + + for example: (result means 'this') + 20 / 3 --> result: 6 remainder: 2 + -20 / 3 --> result: -6 remainder: -2 + 20 / -3 --> result: -6 remainder: 2 + -20 / -3 --> result: 6 remainder: -2 + + in other words: this(old) = ss2 * this(new)(result) + remainder + */ + uint DivInt(sint ss2, sint * remainder = 0) + { + bool ss1_is_sign, ss2_is_sign; + + ss1_is_sign = IsSign(); + + /* + we don't have to test the carry from Abs as well as in Mul + */ + Abs(); + + if( ss2 < 0 ) + { + ss2 = -ss2; + ss2_is_sign = true; + } + else + { + ss2_is_sign = false; + } + + uint rem; + uint c = UInt::DivInt((uint)ss2, &rem); + + if( ss1_is_sign != ss2_is_sign ) + SetSign(); + + if( remainder ) + { + if( ss1_is_sign ) + *remainder = -sint(rem); + else + *remainder = sint(rem); + } + + return c; + } + + + uint DivInt(sint ss2, sint & remainder) + { + return DivInt(ss2, &remainder); + } + + +private: + + + /*! + power this = this ^ pow + this can be negative + pow is >= 0 + */ + uint Pow2(const Int & pow) + { + bool was_sign = IsSign(); + uint c = 0; + + if( was_sign ) + c += Abs(); + + uint c_temp = UInt::Pow(pow); + if( c_temp > 0 ) + return c_temp; // c_temp can be: 0, 1 or 2 + + if( was_sign && (pow.table[0] & 1) == 1 ) + // negative value to the power of odd number is negative + c += ChangeSign(); + + return (c==0)? 0 : 1; + } + + +public: + + + /*! + power this = this ^ pow + + return values: + 0 - ok + 1 - carry + 2 - incorrect arguments 0^0 or 0^(-something) + */ + uint Pow(Int pow) + { + if( !pow.IsSign() ) + return Pow2(pow); + + if( UInt::IsZero() ) + // if 'pow' is negative then + // 'this' must be different from zero + return 2; + + if( pow.ChangeSign() ) + return 1; + + Int t(*this); + uint c_temp = t.Pow2(pow); + if( c_temp > 0 ) + return c_temp; + + UInt::SetOne(); + if( Div(t) ) + return 1; + + return 0; + } + + + + /*! + * + * convertion methods + * + */ +private: + + + /*! + an auxiliary method for converting both from UInt and Int + */ + template + uint FromUIntOrInt(const UInt & p, bool UInt_type) + { + uint min_size = (value_size < argument_size)? value_size : argument_size; + uint i; + + for(i=0 ; i::table[i] = p.table[i]; + + + if( value_size > argument_size ) + { + uint fill; + + if( UInt_type ) + fill = 0; + else + fill = (p.table[argument_size-1] & TTMATH_UINT_HIGHEST_BIT)? + TTMATH_UINT_MAX_VALUE : 0; + + // 'this' is longer than 'p' + for( ; i::table[i] = fill; + } + else + { + uint test = (UInt::table[value_size-1] & TTMATH_UINT_HIGHEST_BIT)? + TTMATH_UINT_MAX_VALUE : 0; + + if( UInt_type && test!=0 ) + return 1; + + for( ; i type into this class + + this operation has mainly sense if the value from p + can be held in this type + + it returns a carry if the value 'p' is too big + */ + template + uint FromInt(const Int & p) + { + return FromUIntOrInt(p, false); + } + + + /*! + this method converts the sint type into this class + */ + uint FromInt(sint value) + { + uint fill = ( value<0 ) ? TTMATH_UINT_MAX_VALUE : 0; + + for(uint i=1 ; i::table[i] = fill; + + UInt::table[0] = uint(value); + + // there'll never be a carry here + return 0; + } + + + /*! + this method converts UInt into this class + */ + template + uint FromUInt(const UInt & p) + { + return FromUIntOrInt(p, true); + } + + + /*! + this method converts UInt into this class + */ + template + uint FromInt(const UInt & p) + { + return FromUIntOrInt(p, true); + } + + + /*! + this method converts the uint type into this class + */ + uint FromUInt(uint value) + { + for(uint i=1 ; i::table[i] = 0; + + UInt::table[0] = value; + + // there can be a carry here when the size of this value is equal one word + // and the 'value' has the highest bit set + if( value_size==1 && (value & TTMATH_UINT_HIGHEST_BIT)!=0 ) + return 1; + + return 0; + } + + + /*! + this method converts the uint type into this class + */ + uint FromInt(uint value) + { + return FromUInt(value); + } + + + /*! + the default assignment operator + */ +/* Int & operator=(const Int & p) + { + FromInt(p); + + return *this; + } + */ + + /*! + this operator converts an Int type to this class + + it doesn't return a carry + */ +/* template + Int & operator=(const Int & p) + { + FromInt(p); + + return *this; + } + */ + + /*! + this method converts the sint type to this class + */ + Int & operator=(sint i) + { + FromInt(i); + + return *this; + } + + + /*! + a constructor for converting the uint to this class + */ +/* Int(sint i) + { + FromInt(i); + } +*/ + + /*! + a copy constructor + */ +/* Int(const Int & u) + { + FromInt(u); + } +*/ + + /*! + a constructor for copying from another types + */ +/* template + Int(const Int & u) + { + // look that 'size' we still set as 'value_size' and not as u.value_size + FromInt(u); + } +*/ + + + /*! + this operator converts an UInt type to this class + + it doesn't return a carry + */ + template + Int & operator=(const UInt & p) + { + FromUInt(p); + + return *this; + } + + + /*! + this method converts the Uint type to this class + */ + Int & operator=(uint i) + { + FromUInt(i); + + return *this; + } + + + /*! + a constructor for converting the uint to this class + */ +/* Int(uint i) + { + FromUInt(i); + } +*/ + + /*! + a constructor for copying from another types + */ +/* template + Int(const UInt & u) + { + // look that 'size' we still set as 'value_size' and not as u.value_size + FromUInt(u); + } +*/ + + +#ifdef TTMATH_PLATFORM32 + + + /*! + this method converts unsigned 64 bit int type to this class + ***this method is created only on a 32bit platform*** + */ + uint FromUInt(ulint n) + { + uint c = UInt::FromUInt(n); + + if( c ) + return 1; + + if( value_size == 1 ) + return ((UInt::table[0] & TTMATH_UINT_HIGHEST_BIT) == 0) ? 0 : 1; + + if( value_size == 2 ) + return ((UInt::table[1] & TTMATH_UINT_HIGHEST_BIT) == 0) ? 0 : 1; + + return 0; + } + + + /*! + this method converts unsigned 64 bit int type to this class + ***this method is created only on a 32bit platform*** + */ + uint FromInt(ulint n) + { + return FromUInt(n); + } + + + /*! + this method converts signed 64 bit int type to this class + ***this method is created only on a 32bit platform*** + */ + uint FromInt(slint n) + { + uint mask = (n < 0) ? TTMATH_UINT_MAX_VALUE : 0; + + UInt::table[0] = (uint)(ulint)n; + + if( value_size == 1 ) + { + if( uint(ulint(n) >> 32) != mask ) + return 1; + + return ((UInt::table[0] & TTMATH_UINT_HIGHEST_BIT) == (mask & TTMATH_UINT_HIGHEST_BIT)) ? 0 : 1; + } + + UInt::table[1] = (uint)(ulint(n) >> 32); + + for(uint i=2 ; i::table[i] = mask; + + return 0; + } + + + /*! + this operator converts unsigned 64 bit int type to this class + ***this operator is created only on a 32bit platform*** + */ + Int & operator=(ulint n) + { + FromUInt(n); + + return *this; + } + + + /*! + a constructor for converting unsigned 64 bit int to this class + ***this constructor is created only on a 32bit platform*** + */ +/* Int(ulint n) + { + FromUInt(n); + } +*/ + + /*! + this operator converts signed 64 bit int type to this class + ***this operator is created only on a 32bit platform*** + */ + Int & operator=(slint n) + { + FromInt(n); + + return *this; + } + + + /*! + a constructor for converting signed 64 bit int to this class + ***this constructor is created only on a 32bit platform*** + */ +/* Int(slint n) + { + FromInt(n); + } +*/ +#endif + + + + +#ifdef TTMATH_PLATFORM64 + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromUInt(unsigned int i) + { + return FromUInt(uint(i)); + } + + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromInt(unsigned int i) + { + return FromUInt(i); + } + + + /*! + this method converts 32 bit signed int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromInt(signed int i) + { + return FromInt(sint(i)); + } + + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + Int & operator=(unsigned int i) + { + FromUInt(i); + + return *this; + } + + + /*! + a constructor for converting 32 bit unsigned int to this class + ***this constructor is created only on a 64bit platform*** + */ +/* Int(unsigned int i) + { + FromUInt(i); + } +*/ + + /*! + this operator converts 32 bit signed int type to this class + ***this operator is created only on a 64bit platform*** + */ + Int & operator=(signed int i) + { + FromInt(i); + + return *this; + } + + + /*! + a constructor for converting 32 bit signed int to this class + ***this constructor is created only on a 64bit platform*** + */ +/* Int(signed int i) + { + FromInt(i); + } +*/ +#endif + + + + /*! + a constructor for converting string to this class (with the base=10) + */ +/* Int(const char * s) + { + FromString(s); + } +*/ + + /*! + a constructor for converting a string to this class (with the base=10) + */ +/* Int(const std::string & s) + { + FromString( s.c_str() ); + } +*/ + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + a constructor for converting string to this class (with the base=10) + */ + Int(const wchar_t * s) + { + FromString(s); + } + + + /*! + a constructor for converting a string to this class (with the base=10) + */ + Int(const std::wstring & s) + { + FromString( s.c_str() ); + } + +#endif + + + /*! + a default constructor + + we don't clear table etc. + */ +/* Int() + { + } +*/ + + /*! + the destructor + */ +/* ~Int() + { + } +*/ + + /*! + this method returns the lowest value from table with a sign + + we must be sure when we using this method whether the value + will hold in an sint type or not (the rest value from table must be zero or -1) + */ + sint ToInt() const + { + return sint( UInt::table[0] ); + } + + + /*! + this method converts the value to uint type + can return a carry if the value is too long to store it in uint type + */ + uint ToUInt(uint & result) const + { + uint c = UInt::ToUInt(result); + + if( value_size == 1 ) + return (result & TTMATH_UINT_HIGHEST_BIT) == 0 ? 0 : 1; + + return c; + } + + + /*! + this method converts the value to uint type + can return a carry if the value is too long to store it in uint type + */ + uint ToInt(uint & result) const + { + return ToUInt(result); + } + + + /*! + this method converts the value to sint type + can return a carry if the value is too long to store it in sint type + */ + uint ToInt(sint & result) const + { + result = sint( UInt::table[0] ); + uint mask = IsSign() ? TTMATH_UINT_MAX_VALUE : 0; + + if( (result & TTMATH_UINT_HIGHEST_BIT) != (mask & TTMATH_UINT_HIGHEST_BIT) ) + return 1; + + for(uint i=1 ; i::table[i] != mask ) + return 1; + + return 0; + } + + +#ifdef TTMATH_PLATFORM32 + + /*! + this method converts the value to ulint type (64 bit unsigned integer) + can return a carry if the value is too long to store it in ulint type + *** this method is created only on a 32 bit platform *** + */ + uint ToUInt(ulint & result) const + { + uint c = UInt::ToUInt(result); + + if( value_size == 1 ) + return (UInt::table[0] & TTMATH_UINT_HIGHEST_BIT) == 0 ? 0 : 1; + + if( value_size == 2 ) + return (UInt::table[1] & TTMATH_UINT_HIGHEST_BIT) == 0 ? 0 : 1; + + return c; + } + + + /*! + this method converts the value to ulint type (64 bit unsigned integer) + can return a carry if the value is too long to store it in ulint type + *** this method is created only on a 32 bit platform *** + */ + uint ToInt(ulint & result) const + { + return ToUInt(result); + } + + + /*! + this method converts the value to slint type (64 bit signed integer) + can return a carry if the value is too long to store it in slint type + *** this method is created only on a 32 bit platform *** + */ + uint ToInt(slint & result) const + { + if( value_size == 1 ) + { + result = slint(sint(UInt::table[0])); + } + else + { + uint low = UInt::table[0]; + uint high = UInt::table[1]; + + result = low; + result |= (ulint(high) << TTMATH_BITS_PER_UINT); + + uint mask = IsSign() ? TTMATH_UINT_MAX_VALUE : 0; + + if( (high & TTMATH_UINT_HIGHEST_BIT) != (mask & TTMATH_UINT_HIGHEST_BIT) ) + return 1; + + for(uint i=2 ; i::table[i] != mask ) + return 1; + } + + return 0; + } + +#endif + + + +#ifdef TTMATH_PLATFORM64 + + /*! + this method converts the value to a 32 bit unsigned integer + can return a carry if the value is too long to store it in this type + *** this method is created only on a 64 bit platform *** + */ + uint ToUInt(unsigned int & result) const + { + uint c = UInt::ToUInt(result); + + if( c || IsSign() ) + return 1; + + return 0; + } + + + /*! + this method converts the value to a 32 bit unsigned integer + can return a carry if the value is too long to store it in this type + *** this method is created only on a 64 bit platform *** + */ + uint ToInt(unsigned int & result) const + { + return ToUInt(result); + } + + + /*! + this method converts the value to a 32 bit signed integer + can return a carry if the value is too long to store it in this type + *** this method is created only on a 64 bit platform *** + */ + uint ToInt(int & result) const + { + uint first = UInt::table[0]; + + result = int(first); + uint mask = IsSign() ? TTMATH_UINT_MAX_VALUE : 0; + + if( (first >> 31) != (mask >> 31) ) + return 1; + + for(uint i=1 ; i::table[i] != mask ) + return 1; + + return 0; + } + +#endif + + + + /*! + an auxiliary method for converting to a string + */ + template + void ToStringBase(string_type & result, uint b = 10) const + { + if( IsSign() ) + { + Int temp(*this); + temp.Abs(); + temp.UInt::ToStringBase(result, b, true); + } + else + { + UInt::ToStringBase(result, b, false); + } + } + + /*! + this method converts the value to a string with a base equal 'b' + */ + void ToString(std::string & result, uint b = 10) const + { + return ToStringBase(result, b); + } + + + /*! + this method converts the value to a string with a base equal 'b' + */ + std::string ToString(uint b = 10) const + { + std::string result; + ToStringBase(result, b); + + return result; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method converts the value to a string with a base equal 'b' + */ + void ToString(std::wstring & result, uint b = 10) const + { + return ToStringBase(result, b); + } + + + /*! + this method converts the value to a string with a base equal 'b' + */ + std::wstring ToWString(uint b = 10) const + { + std::wstring result; + ToStringBase(result, b); + + return result; + } + +#endif + + + +private: + + /*! + an auxiliary method for converting from a string + */ + template + uint FromStringBase(const char_type * s, uint b = 10, const char_type ** after_source = 0, bool * value_read = 0) + { + bool is_sign = false; + + Misc::SkipWhiteCharacters(s); + + if( *s == '-' ) + { + is_sign = true; + Misc::SkipWhiteCharacters(++s); + } + else + if( *s == '+' ) + { + Misc::SkipWhiteCharacters(++s); + } + + if( UInt::FromString(s,b,after_source,value_read) ) + return 1; + + if( is_sign ) + { + Int mmin; + + mmin.SetMin(); + + /* + the reference to mmin will be automatically converted to the reference + to UInt type + (this value can be equal mmin -- look at a description in ChangeSign()) + */ + if( UInt::operator>( mmin ) ) + return 1; + + /* + if the value is equal mmin the method ChangeSign() does nothing (only returns 1 but we ignore it) + */ + ChangeSign(); + } + else + { + Int mmax; + + mmax.SetMax(); + + if( UInt::operator>( mmax ) ) + return 1; + } + + return 0; + } + + +public: + + /*! + this method converts a string into its value + it returns carry=1 if the value will be too big or an incorrect base 'b' is given + + string is ended with a non-digit value, for example: + "-12" will be translated to -12 + as well as: + "- 12foo" will be translated to -12 too + + existing first white characters will be ommited + (between '-' and a first digit can be white characters too) + + after_source (if exists) is pointing at the end of the parsed string + + value_read (if exists) tells whether something has actually been read (at least one digit) + */ + uint FromString(const char * s, uint b = 10, const char ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(s, b, after_source, value_read); + } + + + /*! + this method converts a string into its value + */ + uint FromString(const wchar_t * s, uint b = 10, const wchar_t ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(s, b, after_source, value_read); + } + + + /*! + this method converts a string into its value + it returns carry=1 if the value will be too big or an incorrect base 'b' is given + */ + uint FromString(const std::string & s, uint b = 10) + { + return FromString( s.c_str(), b ); + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + Int & operator=(const char * s) + { + FromString(s); + + return *this; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method converts a string into its value + it returns carry=1 if the value will be too big or an incorrect base 'b' is given + */ + uint FromString(const std::wstring & s, uint b = 10) + { + return FromString( s.c_str(), b ); + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + Int & operator=(const wchar_t * s) + { + FromString(s); + + return *this; + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + Int & operator=(const std::wstring & s) + { + FromString( s.c_str() ); + + return *this; + } + +#endif + + + /*! + this operator converts a string into its value (with base = 10) + */ + Int & operator=(const std::string & s) + { + FromString( s.c_str() ); + + return *this; + } + + + + /*! + * + * methods for comparing + * + * + */ + + bool operator==(const Int & l) const + { + return UInt::operator==(l); + } + + bool operator!=(const Int & l) const + { + return UInt::operator!=(l); + } + + bool operator<(const Int & l) const + { + sint i=value_size-1; + + sint a1 = sint(UInt::table[i]); + sint a2 = sint(l.table[i]); + + if( a1 != a2 ) + return a1 < a2; + + + for(--i ; i>=0 ; --i) + { + if( UInt::table[i] != l.table[i] ) + // comparison as unsigned int + return UInt::table[i] < l.table[i]; + } + + // they're equal + return false; + } + + + bool operator>(const Int & l) const + { + sint i=value_size-1; + + sint a1 = sint(UInt::table[i]); + sint a2 = sint(l.table[i]); + + if( a1 != a2 ) + return a1 > a2; + + + for(--i ; i>=0 ; --i) + { + if( UInt::table[i] != l.table[i] ) + // comparison as unsigned int + return UInt::table[i] > l.table[i]; + } + + // they're equal + return false; + } + + + bool operator<=(const Int & l) const + { + sint i=value_size-1; + + sint a1 = sint(UInt::table[i]); + sint a2 = sint(l.table[i]); + + if( a1 != a2 ) + return a1 < a2; + + + for(--i ; i>=0 ; --i) + { + if( UInt::table[i] != l.table[i] ) + // comparison as unsigned int + return UInt::table[i] < l.table[i]; + } + + // they're equal + return true; + } + + + bool operator>=(const Int & l) const + { + sint i=value_size-1; + + sint a1 = sint(UInt::table[i]); + sint a2 = sint(l.table[i]); + + if( a1 != a2 ) + return a1 > a2; + + + for(--i ; i>=0 ; --i) + { + if( UInt::table[i] != l.table[i] ) + // comparison as unsigned int + return UInt::table[i] > l.table[i]; + } + + // they're equal + return true; + } + + + + /*! + * + * standard mathematical operators + * + */ + + + /*! + an operator for changing the sign + + it's not changing 'this' but the changed value will be returned + */ + Int operator-() const + { + Int temp(*this); + + temp.ChangeSign(); + + return temp; + } + + + Int operator-(const Int & p2) const + { + Int temp(*this); + + temp.Sub(p2); + + return temp; + } + + + Int & operator-=(const Int & p2) + { + Sub(p2); + + return *this; + } + + + Int operator+(const Int & p2) const + { + Int temp(*this); + + temp.Add(p2); + + return temp; + } + + + Int & operator+=(const Int & p2) + { + Add(p2); + + return *this; + } + + + Int operator*(const Int & p2) const + { + Int temp(*this); + + temp.Mul(p2); + + return temp; + } + + + Int & operator*=(const Int & p2) + { + Mul(p2); + + return *this; + } + + + Int operator/(const Int & p2) const + { + Int temp(*this); + + temp.Div(p2); + + return temp; + } + + + Int & operator/=(const Int & p2) + { + Div(p2); + + return *this; + } + + + Int operator%(const Int & p2) const + { + Int temp(*this); + Int remainder; + + temp.Div(p2, remainder); + + return remainder; + } + + + Int & operator%=(const Int & p2) + { + Int remainder; + + Div(p2, remainder); + operator=(remainder); + + return *this; + } + + + /*! + Prefix operator e.g. ++variable + */ + UInt & operator++() + { + AddOne(); + + return *this; + } + + + /*! + Postfix operator e.g. variable++ + */ + UInt operator++(int) + { + UInt temp( *this ); + + AddOne(); + + return temp; + } + + + UInt & operator--() + { + SubOne(); + + return *this; + } + + + UInt operator--(int) + { + UInt temp( *this ); + + SubOne(); + + return temp; + } + + + + /*! + * + * input/output operators for standard streams + * + */ + +private: + + /*! + an auxiliary method for outputing to standard streams + */ + template + static ostream_type & OutputToStream(ostream_type & s, const Int & l) + { + string_type ss; + + l.ToString(ss); + s << ss; + + return s; + } + + + +public: + + + /*! + output to standard streams + */ + friend std::ostream & operator<<(std::ostream & s, const Int & l) + { + return OutputToStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + output to standard streams + */ + friend std::wostream & operator<<(std::wostream & s, const Int & l) + { + return OutputToStream(s, l); + } + +#endif + + + +private: + + /*! + an auxiliary method for converting from a string + */ + template + static istream_type & InputFromStream(istream_type & s, Int & l) + { + string_type ss; + + // char or wchar_t for operator>> + char_type z; + + // operator>> omits white characters if they're set for ommiting + s >> z; + + if( z=='-' || z=='+' ) + { + ss += z; + s >> z; // we're reading a next character (white characters can be ommited) + } + + // we're reading only digits (base=10) + while( s.good() && Misc::CharToDigit(z, 10)>=0 ) + { + ss += z; + z = static_cast(s.get()); + } + + // we're leaving the last readed character + // (it's not belonging to the value) + s.unget(); + + l.FromString(ss); + + return s; + } + + +public: + + /*! + input from standard streams + */ + friend std::istream & operator>>(std::istream & s, Int & l) + { + return InputFromStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + input from standard streams + */ + friend std::wistream & operator>>(std::wistream & s, Int & l) + { + return InputFromStream(s, l); + } +#endif + + +}; + +} // namespace + +#endif diff --git a/extern/ttmath/ttmathmisc.h b/extern/ttmath/ttmathmisc.h new file mode 100644 index 0000000000..330a43a468 --- /dev/null +++ b/extern/ttmath/ttmathmisc.h @@ -0,0 +1,250 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2010, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef headerfilettmathmisc +#define headerfilettmathmisc + + +/*! + \file ttmathmisc.h + \brief some helpful functions +*/ + + +#include + + +namespace ttmath +{ + +/*! + some helpful functions +*/ +class Misc +{ +public: + + +/* + * + * AssignString(result, str) + * result = str + * + */ + +/*! + result = str +*/ +static void AssignString(std::string & result, const char * str) +{ + result = str; +} + + +#ifndef TTMATH_DONT_USE_WCHAR + +/*! + result = str +*/ +static void AssignString(std::wstring & result, const char * str) +{ + result.clear(); + + for( ; *str ; ++str ) + result += *str; +} + + +/*! + result = str +*/ +static void AssignString(std::wstring & result, const std::string & str) +{ + return AssignString(result, str.c_str()); +} + + +/*! + result = str +*/ +static void AssignString(std::string & result, const wchar_t * str) +{ + result.clear(); + + for( ; *str ; ++str ) + result += static_cast(*str); +} + + +/*! + result = str +*/ +static void AssignString(std::string & result, const std::wstring & str) +{ + return AssignString(result, str.c_str()); +} + +#endif + + +/* + * + * AddString(result, str) + * result += str + * + */ + + +/*! + result += str +*/ +static void AddString(std::string & result, const char * str) +{ + result += str; +} + + +#ifndef TTMATH_DONT_USE_WCHAR + +/*! + result += str +*/ +static void AddString(std::wstring & result, const char * str) +{ + for( ; *str ; ++str ) + result += *str; +} + +#endif + + +/* + this method omits any white characters from the string + char_type is char or wchar_t +*/ +template +static void SkipWhiteCharacters(const char_type * & c) +{ + // 13 is at the end in a DOS text file (\r\n) + while( (*c==' ' ) || (*c=='\t') || (*c==13 ) || (*c=='\n') ) + ++c; +} + + + + +/*! + this static method converts one character into its value + + for example: + 1 -> 1 + 8 -> 8 + A -> 10 + f -> 15 + + this method don't check whether c is correct or not +*/ +static uint CharToDigit(uint c) +{ + if(c>='0' && c<='9') + return c-'0'; + + if(c>='a' && c<='z') + return c-'a'+10; + +return c-'A'+10; +} + + +/*! + this method changes a character 'c' into its value + (if there can't be a correct value it returns -1) + + for example: + c=2, base=10 -> function returns 2 + c=A, base=10 -> function returns -1 + c=A, base=16 -> function returns 10 +*/ +static sint CharToDigit(uint c, uint base) +{ + if( c>='0' && c<='9' ) + c=c-'0'; + else + if( c>='a' && c<='z' ) + c=c-'a'+10; + else + if( c>='A' && c<='Z' ) + c=c-'A'+10; + else + return -1; + + + if( c >= base ) + return -1; + + +return sint(c); +} + + + +/*! + this method converts a digit into a char + digit should be from <0,F> + (we don't have to get a base) + + for example: + 1 -> 1 + 8 -> 8 + 10 -> A + 15 -> F +*/ +static uint DigitToChar(uint digit) +{ + if( digit < 10 ) + return digit + '0'; + +return digit - 10 + 'A'; +} + + +}; // struct Misc + +} + + +#endif diff --git a/extern/ttmath/ttmathobjects.h b/extern/ttmath/ttmathobjects.h new file mode 100644 index 0000000000..c35026bbd0 --- /dev/null +++ b/extern/ttmath/ttmathobjects.h @@ -0,0 +1,809 @@ +/* + * This file is a part of TTMath Mathematical Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2010, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef headerfilettmathobject +#define headerfilettmathobject + +/*! + \file ttmathobjects.h + \brief Mathematic functions. +*/ + +#include +#include +#include +#include + +#include "ttmathtypes.h" +#include "ttmathmisc.h" + + +namespace ttmath +{ + +/*! + objects of this class are used with the mathematical parser + they hold variables or functions defined by a user + + each object has its own table in which we're keeping variables or functions +*/ +class Objects +{ +public: + + + /*! + one item (variable or function) + 'items' will be on the table + */ + struct Item + { + // name of a variable of a function + // internally we store variables and funcions as std::string (not std::wstring even when wide characters are used) + std::string value; + + // number of parameters required by the function + // (if there's a variable this 'param' is ignored) + int param; + + Item() {} + Item(const std::string & v, int p) : value(v), param(p) {} + }; + + // 'Table' is the type of our table + typedef std::map Table; + typedef Table::iterator Iterator; + typedef Table::const_iterator CIterator; + + + + /*! + this method returns true if a character 'c' is a character + which can be in a name + + if 'can_be_digit' is true that means when the 'c' is a digit this + method returns true otherwise it returns false + */ + static bool CorrectCharacter(int c, bool can_be_digit) + { + if( (c>='a' && c<='z') || (c>='A' && c<='Z') ) + return true; + + if( can_be_digit && ((c>='0' && c<='9') || c=='_') ) + return true; + + return false; + } + + + /*! + this method returns true if the name can be as a name of an object + */ + template + static bool IsNameCorrect(const string_type & name) + { + if( name.empty() ) + return false; + + if( !CorrectCharacter(name[0], false) ) + return false; + + typename string_type::const_iterator i = name.begin(); + + for(++i ; i!=name.end() ; ++i) + if( !CorrectCharacter(*i, true) ) + return false; + + return true; + } + + + /*! + this method returns true if such an object is defined (name exists) + */ + bool IsDefined(const std::string & name) + { + Iterator i = table.find(name); + + if( i != table.end() ) + // we have this object in our table + return true; + + return false; + } + + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method returns true if such an object is defined (name exists) + */ + bool IsDefined(const std::wstring & name) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return false; + + Misc::AssignString(str_tmp1, name); + + return IsDefined(str_tmp1); + } + +#endif + + + /*! + this method adds one object (variable of function) into the table + */ + ErrorCode Add(const std::string & name, const std::string & value, int param = 0) + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Iterator i = table.find(name); + + if( i != table.end() ) + // we have this object in our table + return err_object_exists; + + table.insert( std::make_pair(name, Item(value, param)) ); + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method adds one object (variable of function) into the table + */ + ErrorCode Add(const std::wstring & name, const std::wstring & value, int param = 0) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + Misc::AssignString(str_tmp2, value); + + return Add(str_tmp1, str_tmp2, param); + } + +#endif + + + /*! + this method returns 'true' if the table is empty + */ + bool Empty() const + { + return table.empty(); + } + + + /*! + this method clears the table + */ + void Clear() + { + return table.clear(); + } + + + /*! + this method returns 'const_iterator' on the first item on the table + */ + CIterator Begin() const + { + return table.begin(); + } + + + /*! + this method returns 'const_iterator' pointing at the space after last item + (returns table.end()) + */ + CIterator End() const + { + return table.end(); + } + + + /*! + this method changes the value and the number of parameters for a specific object + */ + ErrorCode EditValue(const std::string & name, const std::string & value, int param = 0) + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Iterator i = table.find(name); + + if( i == table.end() ) + return err_unknown_object; + + i->second.value = value; + i->second.param = param; + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method changes the value and the number of parameters for a specific object + */ + ErrorCode EditValue(const std::wstring & name, const std::wstring & value, int param = 0) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + Misc::AssignString(str_tmp2, value); + + return EditValue(str_tmp1, str_tmp2, param); + } + +#endif + + + /*! + this method changes the name of a specific object + */ + ErrorCode EditName(const std::string & old_name, const std::string & new_name) + { + if( !IsNameCorrect(old_name) || !IsNameCorrect(new_name) ) + return err_incorrect_name; + + Iterator old_i = table.find(old_name); + if( old_i == table.end() ) + return err_unknown_object; + + if( old_name == new_name ) + // the new name is the same as the old one + // we treat it as a normal situation + return err_ok; + + ErrorCode err = Add(new_name, old_i->second.value, old_i->second.param); + + if( err == err_ok ) + { + old_i = table.find(old_name); + TTMATH_ASSERT( old_i != table.end() ) + + table.erase(old_i); + } + + return err; + } + + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method changes the name of a specific object + */ + ErrorCode EditName(const std::wstring & old_name, const std::wstring & new_name) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(old_name) || !IsNameCorrect(new_name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, old_name); + Misc::AssignString(str_tmp2, new_name); + + return EditName(str_tmp1, str_tmp2); + } + +#endif + + + /*! + this method deletes an object + */ + ErrorCode Delete(const std::string & name) + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Iterator i = table.find(name); + + if( i == table.end() ) + return err_unknown_object; + + table.erase( i ); + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method deletes an object + */ + ErrorCode Delete(const std::wstring & name) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + + return Delete(str_tmp1); + } + +#endif + + + /*! + this method gets the value of a specific object + */ + ErrorCode GetValue(const std::string & name, std::string & value) const + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + CIterator i = table.find(name); + + if( i == table.end() ) + { + value.clear(); + return err_unknown_object; + } + + value = i->second.value; + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method gets the value of a specific object + */ + ErrorCode GetValue(const std::wstring & name, std::wstring & value) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + ErrorCode err = GetValue(str_tmp1, str_tmp2); + Misc::AssignString(value, str_tmp2); + + return err; + } + +#endif + + + /*! + this method gets the value of a specific object + (this version is used for not copying the whole string) + */ + ErrorCode GetValue(const std::string & name, const char ** value) const + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + CIterator i = table.find(name); + + if( i == table.end() ) + { + *value = 0; + return err_unknown_object; + } + + *value = i->second.value.c_str(); + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method gets the value of a specific object + (this version is used for not copying the whole string) + */ + ErrorCode GetValue(const std::wstring & name, const char ** value) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + + return GetValue(str_tmp1, value); + } + +#endif + + + /*! + this method gets the value and the number of parameters + of a specific object + */ + ErrorCode GetValueAndParam(const std::string & name, std::string & value, int * param) const + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + CIterator i = table.find(name); + + if( i == table.end() ) + { + value.empty(); + *param = 0; + return err_unknown_object; + } + + value = i->second.value; + *param = i->second.param; + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method gets the value and the number of parameters + of a specific object + */ + ErrorCode GetValueAndParam(const std::wstring & name, std::wstring & value, int * param) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + ErrorCode err = GetValueAndParam(str_tmp1, str_tmp2, param); + Misc::AssignString(value, str_tmp2); + + return err; + } + +#endif + + + /*! + this method sets the value and the number of parameters + of a specific object + (this version is used for not copying the whole string) + */ + ErrorCode GetValueAndParam(const std::string & name, const char ** value, int * param) const + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + CIterator i = table.find(name); + + if( i == table.end() ) + { + *value = 0; + *param = 0; + return err_unknown_object; + } + + *value = i->second.value.c_str(); + *param = i->second.param; + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method sets the value and the number of parameters + of a specific object + (this version is used for not copying the whole string + but in fact we make one copying during AssignString()) + */ + ErrorCode GetValueAndParam(const std::wstring & name, const char ** value, int * param) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + + return GetValueAndParam(str_tmp1, value, param); + } + + +#endif + + + /*! + this method returns a pointer into the table + */ + Table * GetTable() + { + return &table; + } + + +private: + + Table table; + std::string str_tmp1, str_tmp2; + +}; // end of class Objects + + + + + + + +/*! + objects of the class History are used to keep values in functions + which take a lot of time during calculating, for instance in the + function Factorial(x) + + it means that when we're calculating e.g. Factorial(1000) and the + Factorial finds that we have calculated it before, the value (result) + is taken from the history +*/ +template +class History +{ + /*! + one item in the History's object holds a key, a value for the key + and a corresponding error code + */ + struct Item + { + ValueType key, value; + ErrorCode err; + }; + + + /*! + we use std::list for simply deleting the first item + but because we're searching through the whole container + (in the method Get) the container should not be too big + (linear time of searching) + */ + typedef std::list buffer_type; + buffer_type buffer; + typename buffer_type::size_type buffer_max_size; + +public: + + /*! + default constructor + default max size of the History's container is 15 items + */ + History() + { + buffer_max_size = 15; + } + + + /*! + a constructor which takes another value of the max size + of the History's container + */ + History(typename buffer_type::size_type new_size) + { + buffer_max_size = new_size; + } + + + /*! + this method adds one item into the History + if the size of the container is greater than buffer_max_size + the first item will be removed + */ + void Add(const ValueType & key, const ValueType & value, ErrorCode err) + { + Item item; + item.key = key; + item.value = value; + item.err = err; + + buffer.insert( buffer.end(), item ); + + if( buffer.size() > buffer_max_size ) + buffer.erase(buffer.begin()); + } + + + /*! + this method checks whether we have an item which has the key equal 'key' + + if there's such item the method sets the 'value' and the 'err' + and returns true otherwise it returns false and 'value' and 'err' + remain unchanged + */ + bool Get(const ValueType & key, ValueType & value, ErrorCode & err) + { + typename buffer_type::iterator i = buffer.begin(); + + for( ; i != buffer.end() ; ++i ) + { + if( i->key == key ) + { + value = i->value; + err = i->err; + return true; + } + } + + return false; + } + + + /*! + this methods deletes an item + + we assume that there is only one item with the 'key' + (this methods removes the first one) + */ + bool Remove(const ValueType & key) + { + typename buffer_type::iterator i = buffer.begin(); + + for( ; i != buffer.end() ; ++i ) + { + if( i->key == key ) + { + buffer.erase(i); + return true; + } + } + + return false; + } + + +}; // end of class History + + + +/*! + this is an auxiliary class used when calculating Gamma() or Factorial() + + in multithreaded environment you can provide an object of this class to + the Gamma() or Factorial() function, e.g; + typedef Big<1, 3> MyBig; + MyBig x = 123456; + CGamma cgamma; + std::cout << Gamma(x, cgamma); + each thread should have its own CGamma<> object + + in a single-thread environment a CGamma<> object is a static variable + in a second version of Gamma() and you don't have to explicitly use it, e.g. + typedef Big<1, 3> MyBig; + MyBig x = 123456; + std::cout << Gamma(x); +*/ +template +struct CGamma +{ + /*! + this table holds factorials + 1 + 1 + 2 + 6 + 24 + 120 + 720 + ....... + */ + std::vector fact; + + + /*! + this table holds Bernoulli numbers + 1 + -0.5 + 0.166666666666666666666666667 + 0 + -0.0333333333333333333333333333 + 0 + 0.0238095238095238095238095238 + 0 + -0.0333333333333333333333333333 + 0 + 0.075757575757575757575757576 + ..... + */ + std::vector bern; + + + /*! + here we store some calculated values + (this is for speeding up, if the next argument of Gamma() or Factorial() + is in the 'history' then the result we are not calculating but simply + return from the 'history' object) + */ + History history; + + + /*! + this method prepares some coefficients: factorials and Bernoulli numbers + stored in 'fact' and 'bern' objects + + how many values should be depends on the size of the mantissa - if + the mantissa is larger then we must calculate more values + for a mantissa which consists of 256 bits (8 words on a 32bit platform) + we have to calculate about 30 values (the size of fact and bern will be 30), + and for a 2048 bits mantissa we have to calculate 306 coefficients + + you don't have to call this method, these coefficients will be automatically calculated + when they are needed + + you must note that calculating these coefficients is a little time-consuming operation, + (especially when the mantissa is large) and first call to Gamma() or Factorial() + can take more time than next calls, and in the end this is the point when InitAll() + comes in handy: you can call this method somewhere at the beginning of your program + */ + void InitAll(); + // definition is in ttmath.h +}; + + + + +} // namespace + +#endif diff --git a/extern/ttmath/ttmaththreads.h b/extern/ttmath/ttmaththreads.h new file mode 100644 index 0000000000..586227f2fc --- /dev/null +++ b/extern/ttmath/ttmaththreads.h @@ -0,0 +1,250 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2009, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmaththreads +#define headerfilettmaththreads + +#include "ttmathtypes.h" + +#ifdef TTMATH_WIN32_THREADS +#include +#include +#endif + +#ifdef TTMATH_POSIX_THREADS +#include +#endif + + + +/*! + \file ttmaththreads.h + \brief Some objects used in multithreads environment +*/ + + +/* + this is a simple skeleton of a program in multithreads environment: + + #define TTMATH_MULTITHREADS + #include + + TTMATH_MULTITHREADS_HELPER + + int main() + { + [...] + } + + make sure that macro TTMATH_MULTITHREADS is defined and (somewhere in *.cpp file) + use TTMATH_MULTITHREADS_HELPER macro (outside of any classes/functions/namespaces scope) +*/ + + +namespace ttmath +{ + + +#ifdef TTMATH_WIN32_THREADS + + /* + we use win32 threads + */ + + + /*! + in multithreads environment you should use TTMATH_MULTITHREADS_HELPER macro + somewhere in *.cpp file + + (at the moment in win32 this macro does nothing) + */ + #define TTMATH_MULTITHREADS_HELPER + + + /*! + objects of this class are used to synchronize + */ + class ThreadLock + { + HANDLE mutex_handle; + + + void CreateName(char * buffer) const + { + #ifdef _MSC_VER + #pragma warning (disable : 4996) + // warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. + #endif + + sprintf(buffer, "TTMATH_LOCK_%ul", (unsigned long)GetCurrentProcessId()); + + #ifdef _MSC_VER + #pragma warning (default : 4996) + #endif + } + + + public: + + bool Lock() + { + char buffer[50]; + + CreateName(buffer); + mutex_handle = CreateMutexA(0, false, buffer); + + if( mutex_handle == 0 ) + return false; + + WaitForSingleObject(mutex_handle, INFINITE); + + return true; + } + + + ThreadLock() + { + mutex_handle = 0; + } + + + ~ThreadLock() + { + if( mutex_handle != 0 ) + { + ReleaseMutex(mutex_handle); + CloseHandle(mutex_handle); + } + } + }; + +#endif // #ifdef TTMATH_WIN32_THREADS + + + + + +#ifdef TTMATH_POSIX_THREADS + + /* + we use posix threads + */ + + + /*! + in multithreads environment you should use TTMATH_MULTITHREADS_HELPER macro + somewhere in *.cpp file + (this macro defines a pthread_mutex_t object used by TTMath library) + */ + #define TTMATH_MULTITHREADS_HELPER \ + namespace ttmath \ + { \ + pthread_mutex_t ttmath_mutex = PTHREAD_MUTEX_INITIALIZER; \ + } + + + /*! + ttmath_mutex will be defined by TTMATH_MULTITHREADS_HELPER macro + */ + extern pthread_mutex_t ttmath_mutex; + + + /*! + objects of this class are used to synchronize + */ + class ThreadLock + { + public: + + bool Lock() + { + if( pthread_mutex_lock(&ttmath_mutex) != 0 ) + return false; + + return true; + } + + + ~ThreadLock() + { + pthread_mutex_unlock(&ttmath_mutex); + } + }; + +#endif // #ifdef TTMATH_POSIX_THREADS + + + + +#if !defined(TTMATH_POSIX_THREADS) && !defined(TTMATH_WIN32_THREADS) + + /*! + we don't use win32 and pthreads + */ + + /*! + */ + #define TTMATH_MULTITHREADS_HELPER + + + /*! + objects of this class are used to synchronize + actually we don't synchronize, the method Lock() returns always 'false' + */ + class ThreadLock + { + public: + + bool Lock() + { + return false; + } + }; + + +#endif // #if !defined(TTMATH_POSIX_THREADS) && !defined(TTMATH_WIN32_THREADS) + + + + + +} // namespace + +#endif + diff --git a/extern/ttmath/ttmathtypes.h b/extern/ttmath/ttmathtypes.h new file mode 100644 index 0000000000..3d9ddbe7b0 --- /dev/null +++ b/extern/ttmath/ttmathtypes.h @@ -0,0 +1,676 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2012, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef headerfilettmathtypes +#define headerfilettmathtypes + +/*! + \file ttmathtypes.h + \brief constants used in the library + + As our library is written in header files (templates) we cannot use + constants like 'const int' etc. because we should have some source files + *.cpp to define this variables. Only what we can have are constants + defined by #define preprocessor macros. + + All macros are preceded by TTMATH_ prefix +*/ + + +#include +#include +#include + +#ifndef _MSC_VER +#include +// for uint64_t and int64_t on a 32 bit platform +#endif + + + +/*! + the version of the library + + TTMATH_PRERELEASE_VER is either zero or one + zero means that this is the release version of the library + (one means something like beta) +*/ +#define TTMATH_MAJOR_VER 0 +#define TTMATH_MINOR_VER 9 +#define TTMATH_REVISION_VER 3 + +#define TTMATH_PRERELEASE_VER 0 + + + +/*! + you can define a platform explicitly by defining either + TTMATH_PLATFORM32 or TTMATH_PLATFORM64 macro +*/ +#if !defined TTMATH_PLATFORM32 && !defined TTMATH_PLATFORM64 + + #if !defined _M_X64 && !defined __x86_64__ + + /* + other platforms than x86 and amd64 are not recognized at the moment + so you should set TTMATH_PLATFORMxx manually + */ + + // we're using a 32bit platform + #define TTMATH_PLATFORM32 + + #else + + // we're using a 64bit platform + #define TTMATH_PLATFORM64 + + #endif + +#endif + + +/*! + asm version of the library is available by default only for: + x86 and amd64 platforms and for Microsoft Visual and GCC compilers + + but you can force using asm version (the same asm as for Microsoft Visual) + by defining TTMATH_FORCEASM macro + you have to be sure that your compiler accept such an asm format +*/ +#ifndef TTMATH_FORCEASM + + #if !defined __i386__ && !defined _X86_ && !defined _M_IX86 && !defined __x86_64__ && !defined _M_X64 + /*! + x86 architecture: + __i386__ defined by GNU C + _X86_ defined by MinGW32 + _M_IX86 defined by Visual Studio, Intel C/C++, Digital Mars and Watcom C/C++ + + amd64 architecture: + __x86_64__ defined by GNU C, CLANG (LLVM) and Sun Studio + _M_X64 defined by Visual Studio + + asm version is available only for x86 or amd64 platforms + */ + #define TTMATH_NOASM + #endif + + + + #if !defined _MSC_VER && !defined __GNUC__ + /*! + another compilers than MS VC or GCC or CLANG (LLVM) by default use no asm version + (CLANG defines __GNUC__ too) + */ + #define TTMATH_NOASM + #endif + +#endif + + +namespace ttmath +{ + + +#ifdef TTMATH_PLATFORM32 + + /*! + on 32bit platforms one word (uint, sint) will be equal 32bits + */ + typedef unsigned int uint; + typedef signed int sint; + + /*! + on 32 bit platform ulint and slint will be equal 64 bits + */ + #ifdef _MSC_VER + // long long on MS Windows (Visual and GCC mingw compilers) have 64 bits + // stdint.h is not available on Visual Studio prior to VS 2010 version + typedef unsigned long long int ulint; + typedef signed long long int slint; + #else + // we do not use 'long' here because there is a difference in unix and windows + // environments: in unix 'long' has 64 bits but in windows it has only 32 bits + typedef uint64_t ulint; + typedef int64_t slint; + #endif + + /*! + how many bits there are in the uint type + */ + #define TTMATH_BITS_PER_UINT 32u + + /*! + the mask for the highest bit in the unsigned 32bit word (2^31) + */ + #define TTMATH_UINT_HIGHEST_BIT 2147483648u + + /*! + the max value of the unsigned 32bit word (2^32 - 1) + (all bits equal one) + */ + #define TTMATH_UINT_MAX_VALUE 4294967295u + + /*! + the number of words (32bit words on 32bit platform) + which are kept in built-in variables for a Big<> type + (these variables are defined in ttmathbig.h) + */ + #define TTMATH_BUILTIN_VARIABLES_SIZE 256u + + /*! + this macro returns the number of machine words + capable to hold min_bits bits + e.g. TTMATH_BITS(128) returns 4 + */ + #define TTMATH_BITS(min_bits) ((min_bits-1)/32 + 1) + +#else + + /*! + on 64bit platforms one word (uint, sint) will be equal 64bits + */ + #ifdef _MSC_VER + /* in VC 'long' type has 32 bits, __int64 is VC extension */ + typedef unsigned __int64 uint; + typedef signed __int64 sint; + #else + typedef unsigned long uint; + typedef signed long sint; + #endif + + /*! + on 64bit platforms we do not define ulint and slint + */ + + /*! + how many bits there are in the uint type + */ + #define TTMATH_BITS_PER_UINT 64ul + + /*! + the mask for the highest bit in the unsigned 64bit word (2^63) + */ + #define TTMATH_UINT_HIGHEST_BIT 9223372036854775808ul + + /*! + the max value of the unsigned 64bit word (2^64 - 1) + (all bits equal one) + */ + #define TTMATH_UINT_MAX_VALUE 18446744073709551615ul + + /*! + the number of words (64bit words on 64bit platforms) + which are kept in built-in variables for a Big<> type + (these variables are defined in ttmathbig.h) + */ + #define TTMATH_BUILTIN_VARIABLES_SIZE 128ul + + /*! + this macro returns the number of machine words + capable to hold min_bits bits + e.g. TTMATH_BITS(128) returns 2 + */ + #define TTMATH_BITS(min_bits) ((min_bits-1)/64 + 1) + +#endif +} + + +#if defined(TTMATH_MULTITHREADS) && !defined(TTMATH_MULTITHREADS_NOSYNC) + #if !defined(TTMATH_POSIX_THREADS) && !defined(TTMATH_WIN32_THREADS) + + #if defined(_WIN32) + #define TTMATH_WIN32_THREADS + #elif defined(unix) || defined(__unix__) || defined(__unix) + #define TTMATH_POSIX_THREADS + #endif + + #endif +#endif + + + +/*! + this variable defines how many iterations are performed + during some kind of calculating when we're making any long formulas + (for example Taylor series) + + it's used in ExpSurrounding0(...), LnSurrounding1(...), Sin0pi05(...), etc. + + note! there'll not be so many iterations, iterations are stopped when + there is no sense to continue calculating (for example when the result + still remains unchanged after adding next series and we know that the next + series are smaller than previous ones) +*/ +#define TTMATH_ARITHMETIC_MAX_LOOP 10000 + + + +/*! + this is a limit when calculating Karatsuba multiplication + if the size of a vector is smaller than TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE + the Karatsuba algorithm will use standard schoolbook multiplication +*/ +#ifdef TTMATH_DEBUG_LOG + // if TTMATH_DEBUG_LOG is defined then we should use the same size regardless of the compiler + #define TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE 3 +#else + #ifdef __GNUC__ + #define TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE 3 + #else + #define TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE 5 + #endif +#endif + + +/*! + this is a special value used when calculating the Gamma(x) function + if x is greater than this value then the Gamma(x) will be calculated using + some kind of series + + don't use smaller values than about 100 +*/ +#define TTMATH_GAMMA_BOUNDARY 2000 + + + + + +namespace ttmath +{ + + /*! + lib type codes: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + */ + enum LibTypeCode + { + asm_vc_32 = 0, + asm_gcc_32, + asm_vc_64, + asm_gcc_64, + no_asm_32, + no_asm_64 + }; + + + /*! + error codes + */ + enum ErrorCode + { + err_ok = 0, + err_nothing_has_read, + err_unknown_character, + err_unexpected_final_bracket, + err_stack_not_clear, + err_unknown_variable, + err_division_by_zero, + err_interrupt, + err_overflow, + err_unknown_function, + err_unknown_operator, + err_unexpected_semicolon_operator, + err_improper_amount_of_arguments, + err_improper_argument, + err_unexpected_end, + err_internal_error, + err_incorrect_name, + err_incorrect_value, + err_variable_exists, + err_variable_loop, + err_functions_loop, + err_must_be_only_one_value, + err_object_exists, + err_unknown_object, + err_still_calculating, + err_in_short_form_used_function, + err_percent_from + }; + + + /*! + this struct is used when converting to/from a string + /temporarily only in Big::ToString() and Big::FromString()/ + */ + struct Conv + { + /*! + base (radix) on which the value will be shown (or read) + default: 10 + */ + uint base; + + + /*! + used only in Big::ToString() + if true the value will be always shown in the scientific mode, e.g: 123e+30 + default: false + */ + bool scient; + + + /*! + used only in Big::ToString() + if scient is false then the value will be printed in the scientific mode + only if the exponent is greater than scien_from + default: 15 + */ + sint scient_from; + + + /*! + if 'base_round' is true and 'base' is different from 2, 4, 8, or 16 + and the result value is not an integer then we make an additional rounding + (after converting the last digit from the result is skipped) + default: true + + e.g. + Conv c; + c.base_round = false; + Big<1, 1> a = "0.1"; // decimal input + std::cout << a.ToString(c) << std::endl; // the result is: 0.099999999 + */ + bool base_round; + + + /*! + used only in Big::ToString() + tells how many digits after comma are possible + default: -1 which means all digits are printed + + set it to zero if you want integer value only + + for example when the value is: + 12.345678 and 'round' is 4 + then the result will be + 12.3457 (the last digit was rounded) + */ + sint round; + + + /*! + if true that not mattered digits in the mantissa will be cut off + (zero characters at the end -- after the comma operator) + e.g. 1234,78000 will be: 1234,78 + default: true + */ + bool trim_zeroes; + + + /*! + the main comma operator (used when reading and writing) + default is a dot '.' + */ + uint comma; + + + /*! + additional comma operator (used only when reading) + if you don't want it just set it to zero + default is a comma ',' + + this allowes you to convert from a value: + 123.45 as well as from 123,45 + */ + uint comma2; + + + /*! + it sets the character which is used for grouping + if group=' ' then: 1234,56789 will be printed as: 1 234,567 89 + + if you don't want grouping just set it to zero (which is default) + */ + uint group; + + + /*! + how many digits should be grouped (it is used if 'group' is non zero) + default: 3 + */ + uint group_digits; + + + /*! + */ + uint group_exp; // not implemented yet + + + + + Conv() + { + // default values + base = 10; + scient = false; + scient_from = 15; + base_round = true; + round = -1; + trim_zeroes = true; + comma = '.'; + comma2 = ','; + group = 0; + group_digits = 3; + group_exp = 0; + } + }; + + + + /*! + this simple class can be used in multithreading model + (you can write your own class derived from this one) + + for example: in some functions like Factorial() + /at the moment only Factorial/ you can give a pointer to + the 'stop object', if the method WasStopSignal() of this + object returns true that means we should break the calculating + and return + */ + class StopCalculating + { + public: + virtual bool WasStopSignal() const volatile { return false; } + virtual ~StopCalculating(){} + }; + + + /*! + a small class which is useful when compiling with gcc + + object of this type holds the name and the line of a file + in which the macro TTMATH_ASSERT or TTMATH_REFERENCE_ASSERT was used + */ + class ExceptionInfo + { + const char * file; + int line; + + public: + ExceptionInfo() : file(0), line(0) {} + ExceptionInfo(const char * f, int l) : file(f), line(l) {} + + std::string Where() const + { + if( !file ) + return "unknown"; + + std::ostringstream result; + result << file << ":" << line; + + return result.str(); + } + }; + + + /*! + A small class used for reporting 'reference' errors + + In the library is used macro TTMATH_REFERENCE_ASSERT which + can throw an exception of this type + + ** from version 0.9.2 this macro is removed from all methods + in public interface so you don't have to worry about it ** + + If you compile with gcc you can get a small benefit + from using method Where() (it returns std::string) with + the name and the line of a file where the macro TTMATH_REFERENCE_ASSERT + was used) + */ + class ReferenceError : public std::logic_error, public ExceptionInfo + { + public: + + ReferenceError() : std::logic_error("reference error") + { + } + + ReferenceError(const char * f, int l) : + std::logic_error("reference error"), ExceptionInfo(f,l) + { + } + + std::string Where() const + { + return ExceptionInfo::Where(); + } + }; + + + /*! + a small class used for reporting errors + + in the library is used macro TTMATH_ASSERT which + (if the condition in it is false) throw an exception + of this type + + if you compile with gcc you can get a small benefit + from using method Where() (it returns std::string) with + the name and the line of a file where the macro TTMATH_ASSERT + was used) + */ + class RuntimeError : public std::runtime_error, public ExceptionInfo + { + public: + + RuntimeError() : std::runtime_error("internal error") + { + } + + RuntimeError(const char * f, int l) : + std::runtime_error("internal error"), ExceptionInfo(f,l) + { + } + + std::string Where() const + { + return ExceptionInfo::Where(); + } + }; + + + + /*! + TTMATH_DEBUG + this macro enables further testing during writing your code + you don't have to define it in a release mode + + if this macro is set then macros TTMATH_ASSERT and TTMATH_REFERENCE_ASSERT + are set as well and these macros can throw an exception if a condition in it + is not fulfilled (look at the definition of TTMATH_ASSERT and TTMATH_REFERENCE_ASSERT) + + TTMATH_DEBUG is set automatically if DEBUG or _DEBUG are defined + */ + #if defined DEBUG || defined _DEBUG + #define TTMATH_DEBUG + #endif + + + #ifdef TTMATH_DEBUG + + #if defined(__FILE__) && defined(__LINE__) + + #define TTMATH_REFERENCE_ASSERT(expression) \ + if( &(expression) == this ) throw ttmath::ReferenceError(__FILE__, __LINE__); + + #define TTMATH_ASSERT(expression) \ + if( !(expression) ) throw ttmath::RuntimeError(__FILE__, __LINE__); + + #else + + #define TTMATH_REFERENCE_ASSERT(expression) \ + if( &(expression) == this ) throw ReferenceError(); + + #define TTMATH_ASSERT(expression) \ + if( !(expression) ) throw RuntimeError(); + #endif + + #else + #define TTMATH_REFERENCE_ASSERT(expression) + #define TTMATH_ASSERT(expression) + #endif + + + + #ifdef TTMATH_DEBUG_LOG + #define TTMATH_LOG(msg) PrintLog(msg, std::cout); + #define TTMATH_LOGC(msg, carry) PrintLog(msg, carry, std::cout); + #define TTMATH_VECTOR_LOG(msg, vector, len) PrintVectorLog(msg, std::cout, vector, len); + #define TTMATH_VECTOR_LOGC(msg, carry, vector, len) PrintVectorLog(msg, carry, std::cout, vector, len); + #else + #define TTMATH_LOG(msg) + #define TTMATH_LOGC(msg, carry) + #define TTMATH_VECTOR_LOG(msg, vector, len) + #define TTMATH_VECTOR_LOGC(msg, carry, vector, len) + #endif + + + + +} // namespace + + +#endif + diff --git a/extern/ttmath/ttmathuint.h b/extern/ttmath/ttmathuint.h new file mode 100644 index 0000000000..b9cf67cd11 --- /dev/null +++ b/extern/ttmath/ttmathuint.h @@ -0,0 +1,4126 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2011, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmathuint +#define headerfilettmathuint + + +/*! + \file ttmathuint.h + \brief template class UInt +*/ + +#include +#include + + +#include "ttmathtypes.h" +#include "ttmathmisc.h" + + + +/*! + \brief a namespace for the TTMath library +*/ +namespace ttmath +{ + +/*! + \brief UInt implements a big integer value without a sign + + value_size - how many bytes specify our value + on 32bit platforms: value_size=1 -> 4 bytes -> 32 bits + on 64bit platforms: value_size=1 -> 8 bytes -> 64 bits + value_size = 1,2,3,4,5,6.... +*/ +template +class UInt +{ +public: + + /*! + buffer for the integer value + table[0] - the lowest word of the value + */ + uint table[value_size]; + + + + /*! + some methods used for debugging purposes + */ + + + /*! + this method is used when macro TTMATH_DEBUG_LOG is defined + */ + template + static void PrintVectorLog(const char_type * msg, ostream_type & output, const uint * vector, uint vector_len) + { + output << msg << std::endl; + + for(uint i=0 ; i + static void PrintVectorLog(const char_type * msg, uint carry, ostream_type & output, const uint * vector, uint vector_len) + { + PrintVectorLog(msg, output, vector, vector_len); + output << " carry: " << carry << std::endl; + } + + + /*! + this method is used when macro TTMATH_DEBUG_LOG is defined + */ + template + void PrintLog(const char_type * msg, ostream_type & output) const + { + PrintVectorLog(msg, output, table, value_size); + } + + + /*! + this method is used when macro TTMATH_DEBUG_LOG is defined + */ + template + void PrintLog(const char_type * msg, uint carry, ostream_type & output) const + { + PrintVectorLog(msg, output, table, value_size); + output << " carry: " << carry << std::endl; + } + + + /*! + this method returns the size of the table + */ + uint Size() const + { + return value_size; + } + + + /*! + this method sets zero + */ + void SetZero() + { + // in the future here can be 'memset' + + for(uint i=0 ; i & ss2) + { + for(uint i=0 ; i=0 && temp_table_index=0 ; --i) + table[i] = 0; + + + TTMATH_LOG("UInt::SetFromTable") + } + +#endif + + +#ifdef TTMATH_PLATFORM64 + /*! + this method copies the value stored in an another table + (warning: first values in temp_table are the highest words -- it's different + from our table) + + ***this method is created only on a 64bit platform*** + + we copy as many words as it is possible + + if temp_table_len is bigger than value_size we'll try to round + the lowest word from table depending on the last not used bit in temp_table + (this rounding isn't a perfect rounding -- look at the description below) + + and if temp_table_len is smaller than value_size we'll clear the rest words + in the table + + warning: we're using 'temp_table' as a pointer at 32bit words + */ + void SetFromTable(const unsigned int * temp_table, uint temp_table_len) + { + uint temp_table_index = 0; + sint i; // 'i' with a sign + + for(i=value_size-1 ; i>=0 && temp_table_index= 0 ; --i) + table[i] = 0; + + TTMATH_LOG("UInt::SetFromTable") + } + +#endif + + + + + + /*! + * + * basic mathematic functions + * + */ + + + + + /*! + this method adds one to the existing value + */ + uint AddOne() + { + return AddInt(1); + } + + + /*! + this method subtracts one from the existing value + */ + uint SubOne() + { + return SubInt(1); + } + + +private: + + + /*! + an auxiliary method for moving bits into the left hand side + + this method moves only words + */ + void RclMoveAllWords(uint & rest_bits, uint & last_c, uint bits, uint c) + { + rest_bits = bits % TTMATH_BITS_PER_UINT; + uint all_words = bits / TTMATH_BITS_PER_UINT; + uint mask = ( c ) ? TTMATH_UINT_MAX_VALUE : 0; + + + if( all_words >= value_size ) + { + if( all_words == value_size && rest_bits == 0 ) + last_c = table[0] & 1; + // else: last_c is default set to 0 + + // clearing + for(uint i = 0 ; i 0 ) + { + // 0 < all_words < value_size + + sint first, second; + last_c = table[value_size - all_words] & 1; // all_words is greater than 0 + + // copying the first part of the value + for(first = value_size-1, second=first-all_words ; second>=0 ; --first, --second) + table[first] = table[second]; + + // setting the rest to 'c' + for( ; first>=0 ; --first ) + table[first] = mask; + } + + TTMATH_LOG("UInt::RclMoveAllWords") + } + +public: + + /*! + moving all bits into the left side 'bits' times + return value <- this <- C + + bits is from a range of <0, man * TTMATH_BITS_PER_UINT> + or it can be even bigger then all bits will be set to 'c' + + the value c will be set into the lowest bits + and the method returns state of the last moved bit + */ + uint Rcl(uint bits, uint c=0) + { + uint last_c = 0; + uint rest_bits = bits; + + if( bits == 0 ) + return 0; + + if( bits >= TTMATH_BITS_PER_UINT ) + RclMoveAllWords(rest_bits, last_c, bits, c); + + if( rest_bits == 0 ) + { + TTMATH_LOG("UInt::Rcl") + return last_c; + } + + // rest_bits is from 1 to TTMATH_BITS_PER_UINT-1 now + if( rest_bits == 1 ) + { + last_c = Rcl2_one(c); + } + else if( rest_bits == 2 ) + { + // performance tests showed that for rest_bits==2 it's better to use Rcl2_one twice instead of Rcl2(2,c) + Rcl2_one(c); + last_c = Rcl2_one(c); + } + else + { + last_c = Rcl2(rest_bits, c); + } + + TTMATH_LOGC("UInt::Rcl", last_c) + + return last_c; + } + +private: + + /*! + an auxiliary method for moving bits into the right hand side + + this method moves only words + */ + void RcrMoveAllWords(uint & rest_bits, uint & last_c, uint bits, uint c) + { + rest_bits = bits % TTMATH_BITS_PER_UINT; + uint all_words = bits / TTMATH_BITS_PER_UINT; + uint mask = ( c ) ? TTMATH_UINT_MAX_VALUE : 0; + + + if( all_words >= value_size ) + { + if( all_words == value_size && rest_bits == 0 ) + last_c = (table[value_size-1] & TTMATH_UINT_HIGHEST_BIT) ? 1 : 0; + // else: last_c is default set to 0 + + // clearing + for(uint i = 0 ; i 0 ) + { + // 0 < all_words < value_size + + uint first, second; + last_c = (table[all_words - 1] & TTMATH_UINT_HIGHEST_BIT) ? 1 : 0; // all_words is > 0 + + // copying the first part of the value + for(first=0, second=all_words ; second this -> return value + + bits is from a range of <0, man * TTMATH_BITS_PER_UINT> + or it can be even bigger then all bits will be set to 'c' + + the value c will be set into the highest bits + and the method returns state of the last moved bit + */ + uint Rcr(uint bits, uint c=0) + { + uint last_c = 0; + uint rest_bits = bits; + + if( bits == 0 ) + return 0; + + if( bits >= TTMATH_BITS_PER_UINT ) + RcrMoveAllWords(rest_bits, last_c, bits, c); + + if( rest_bits == 0 ) + { + TTMATH_LOG("UInt::Rcr") + return last_c; + } + + // rest_bits is from 1 to TTMATH_BITS_PER_UINT-1 now + if( rest_bits == 1 ) + { + last_c = Rcr2_one(c); + } + else if( rest_bits == 2 ) + { + // performance tests showed that for rest_bits==2 it's better to use Rcr2_one twice instead of Rcr2(2,c) + Rcr2_one(c); + last_c = Rcr2_one(c); + } + else + { + last_c = Rcr2(rest_bits, c); + } + + TTMATH_LOGC("UInt::Rcr", last_c) + + return last_c; + } + + + /*! + this method moves all bits into the left side + (it returns value how many bits have been moved) + */ + uint CompensationToLeft() + { + uint moving = 0; + + // a - index a last word which is different from zero + sint a; + for(a=value_size-1 ; a>=0 && table[a]==0 ; --a); + + if( a < 0 ) + return moving; // all words in table have zero + + if( a != value_size-1 ) + { + moving += ( value_size-1 - a ) * TTMATH_BITS_PER_UINT; + + // moving all words + sint i; + for(i=value_size-1 ; a>=0 ; --i, --a) + table[i] = table[a]; + + // setting the rest word to zero + for(; i>=0 ; --i) + table[i] = 0; + } + + uint moving2 = FindLeadingBitInWord( table[value_size-1] ); + // moving2 is different from -1 because the value table[value_size-1] + // is not zero + + moving2 = TTMATH_BITS_PER_UINT - moving2 - 1; + Rcl(moving2); + + TTMATH_LOG("UInt::CompensationToLeft") + + return moving + moving2; + } + + + /*! + this method looks for the highest set bit + + result: + if 'this' is not zero: + return value - true + 'table_id' - the index of a word <0..value_size-1> + 'index' - the index of this set bit in the word <0..TTMATH_BITS_PER_UINT) + + if 'this' is zero: + return value - false + both 'table_id' and 'index' are zero + */ + bool FindLeadingBit(uint & table_id, uint & index) const + { + for(table_id=value_size-1 ; table_id!=0 && table[table_id]==0 ; --table_id); + + if( table_id==0 && table[table_id]==0 ) + { + // is zero + index = 0; + + return false; + } + + // table[table_id] is different from 0 + index = FindLeadingBitInWord( table[table_id] ); + + return true; + } + + + /*! + this method looks for the smallest set bit + + result: + if 'this' is not zero: + return value - true + 'table_id' - the index of a word <0..value_size-1> + 'index' - the index of this set bit in the word <0..TTMATH_BITS_PER_UINT) + + if 'this' is zero: + return value - false + both 'table_id' and 'index' are zero + */ + bool FindLowestBit(uint & table_id, uint & index) const + { + for(table_id=0 ; table_id= value_size ) + { + // is zero + index = 0; + table_id = 0; + + return false; + } + + // table[table_id] is different from 0 + index = FindLowestBitInWord( table[table_id] ); + + return true; + } + + + /*! + getting the 'bit_index' bit + + bit_index bigger or equal zero + */ + uint GetBit(uint bit_index) const + { + TTMATH_ASSERT( bit_index < value_size * TTMATH_BITS_PER_UINT ) + + uint index = bit_index / TTMATH_BITS_PER_UINT; + uint bit = bit_index % TTMATH_BITS_PER_UINT; + + uint temp = table[index]; + uint res = SetBitInWord(temp, bit); + + return res; + } + + + /*! + setting the 'bit_index' bit + and returning the last state of the bit + + bit_index bigger or equal zero + */ + uint SetBit(uint bit_index) + { + TTMATH_ASSERT( bit_index < value_size * TTMATH_BITS_PER_UINT ) + + uint index = bit_index / TTMATH_BITS_PER_UINT; + uint bit = bit_index % TTMATH_BITS_PER_UINT; + uint res = SetBitInWord(table[index], bit); + + TTMATH_LOG("UInt::SetBit") + + return res; + } + + + /*! + this method performs a bitwise operation AND + */ + void BitAnd(const UInt & ss2) + { + for(uint x=0 ; x & ss2) + { + for(uint x=0 ; x & ss2) + { + for(uint x=0 ; x + + for example: + BitNot2(8) = BitNot2( 1000(bin) ) = 111(bin) = 7 + */ + void BitNot2() + { + uint table_id, index; + + if( FindLeadingBit(table_id, index) ) + { + for(uint x=0 ; x>= shift; + + table[table_id] ^= mask; + } + else + table[0] = 1; + + + TTMATH_LOG("UInt::BitNot2") + } + + + + /*! + * + * Multiplication + * + * + */ + +public: + + /*! + multiplication: this = this * ss2 + + it can return a carry + */ + uint MulInt(uint ss2) + { + uint r1, r2, x1; + uint c = 0; + + UInt u(*this); + SetZero(); + + if( ss2 == 0 ) + { + TTMATH_LOGC("UInt::MulInt(uint)", 0) + return 0; + } + + for(x1=0 ; x1 + void MulInt(uint ss2, UInt & result) const + { + TTMATH_ASSERT( result_size > value_size ) + + uint r2,r1; + uint x1size=value_size; + uint x1start=0; + + result.SetZero(); + + if( ss2 == 0 ) + { + TTMATH_VECTOR_LOG("UInt::MulInt(uint, UInt<>)", result.table, result_size) + return; + } + + if( value_size > 2 ) + { + // if the value_size is smaller than or equal to 2 + // there is no sense to set x1size and x1start to another values + + for(x1size=value_size ; x1size>0 && table[x1size-1]==0 ; --x1size); + + if( x1size == 0 ) + { + TTMATH_VECTOR_LOG("UInt::MulInt(uint, UInt<>)", result.table, result_size) + return; + } + + for(x1start=0 ; x1start)", result.table, result_size) + + return; + } + + + + /*! + the multiplication 'this' = 'this' * ss2 + + algorithm: 100 - means automatically choose the fastest algorithm + */ + uint Mul(const UInt & ss2, uint algorithm = 100) + { + switch( algorithm ) + { + case 1: + return Mul1(ss2); + + case 2: + return Mul2(ss2); + + case 3: + return Mul3(ss2); + + case 100: + default: + return MulFastest(ss2); + } + } + + + /*! + the multiplication 'result' = 'this' * ss2 + + since the 'result' is twice bigger than 'this' and 'ss2' + this method never returns a carry + + algorithm: 100 - means automatically choose the fastest algorithm + */ + void MulBig(const UInt & ss2, + UInt & result, + uint algorithm = 100) + { + switch( algorithm ) + { + case 1: + return Mul1Big(ss2, result); + + case 2: + return Mul2Big(ss2, result); + + case 3: + return Mul3Big(ss2, result); + + case 100: + default: + return MulFastestBig(ss2, result); + } + } + + + + /*! + the first version of the multiplication algorithm + */ + +private: + + /*! + multiplication: this = this * ss2 + + it returns carry if it has been + */ + uint Mul1Ref(const UInt & ss2) + { + TTMATH_REFERENCE_ASSERT( ss2 ) + + UInt ss1( *this ); + SetZero(); + + for(uint i=0; i < value_size*TTMATH_BITS_PER_UINT ; ++i) + { + if( Add(*this) ) + { + TTMATH_LOGC("UInt::Mul1", 1) + return 1; + } + + if( ss1.Rcl(1) ) + if( Add(ss2) ) + { + TTMATH_LOGC("UInt::Mul1", 1) + return 1; + } + } + + TTMATH_LOGC("UInt::Mul1", 0) + + return 0; + } + + +public: + + /*! + multiplication: this = this * ss2 + can return carry + */ + uint Mul1(const UInt & ss2) + { + if( this == &ss2 ) + { + UInt copy_ss2(ss2); + return Mul1Ref(copy_ss2); + } + else + { + return Mul1Ref(ss2); + } + } + + + /*! + multiplication: result = this * ss2 + + result is twice bigger than 'this' and 'ss2' + this method never returns carry + */ + void Mul1Big(const UInt & ss2_, UInt & result) + { + UInt ss2; + uint i; + + // copying *this into result and ss2_ into ss2 + for(i=0 ; i & ss2) + { + UInt result; + uint i, c = 0; + + Mul2Big(ss2, result); + + // copying result + for(i=0 ; i & ss2, UInt & result) + { + Mul2Big2(table, ss2.table, result); + + TTMATH_LOG("UInt::Mul2Big") + } + + +private: + + /*! + an auxiliary method for calculating the multiplication + + arguments we're taking as pointers (this is to improve the Mul3Big2()- avoiding + unnecessary copying objects), the result should be taken as a pointer too, + but at the moment there is no method AddTwoInts() which can operate on pointers + */ + template + void Mul2Big2(const uint * ss1, const uint * ss2, UInt & result) + { + uint x1size = ss_size, x2size = ss_size; + uint x1start = 0, x2start = 0; + + if( ss_size > 2 ) + { + // if the ss_size is smaller than or equal to 2 + // there is no sense to set x1size (and others) to another values + + for(x1size=ss_size ; x1size>0 && ss1[x1size-1]==0 ; --x1size); + for(x2size=ss_size ; x2size>0 && ss2[x2size-1]==0 ; --x2size); + + for(x1start=0 ; x1start(ss1, ss2, result, x1start, x1size, x2start, x2size); + } + + + + /*! + an auxiliary method for calculating the multiplication + */ + template + void Mul2Big3(const uint * ss1, const uint * ss2, UInt & result, uint x1start, uint x1size, uint x2start, uint x2size) + { + uint r2, r1; + + result.SetZero(); + + if( x1size==0 || x2size==0 ) + return; + + for(uint x1=x1start ; x1 & ss2) + { + UInt result; + uint i, c = 0; + + Mul3Big(ss2, result); + + // copying result + for(i=0 ; i & ss2, UInt & result) + { + Mul3Big2(table, ss2.table, result.table); + + TTMATH_LOG("UInt::Mul3Big") + } + + + +private: + + /*! + an auxiliary method for calculating the Karatsuba multiplication + + result_size is equal ss_size*2 + */ + template + void Mul3Big2(const uint * ss1, const uint * ss2, uint * result) + { + const uint * x1, * x0, * y1, * y0; + + + if( ss_size>1 && ss_size res; + Mul2Big2(ss1, ss2, res); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + + for(uint i=0 ; i(x1, x0, y1, y0, result); + } + else + { + // ss_size is even + x0 = ss1; + y0 = ss2; + x1 = ss1 + ss_size / 2; + y1 = ss2 + ss_size / 2; + + // all four vectors (x0 x1 y0 y1) are equal in size + Mul3Big3(x1, x0, y1, y0, result); + } + } + + + +#ifdef _MSC_VER +#pragma warning (disable : 4717) +//warning C4717: recursive on all control paths, function will cause runtime stack overflow +//we have the stop point in Mul3Big2() method +#endif + + + /*! + an auxiliary method for calculating the Karatsuba multiplication + + x = x1*B^m + x0 + y = y1*B^m + y0 + + first_size - is the size of vectors: x0 and y0 + second_size - is the size of vectors: x1 and y1 (can be either equal first_size or smaller about one from first_size) + + x*y = (x1*B^m + x0)(y1*B^m + y0) = z2*B^(2m) + z1*B^m + z0 + where + z0 = x0*y0 + z2 = x1*y1 + z1 = (x1 + x0)*(y1 + y0) - z2 - z0 + */ + template + uint Mul3Big3(const uint * x1, const uint * x0, const uint * y1, const uint * y0, uint * result) + { + uint i, c, xc, yc; + + UInt temp, temp2; + UInt z1; + + // z0 and z2 we store directly in the result (we don't use any temporary variables) + Mul3Big2(x0, y0, result); // z0 + Mul3Big2(x1, y1, result+first_size*2); // z2 + + // now we calculate z1 + // temp = (x0 + x1) + // temp2 = (y0 + y1) + // we're using temp and temp2 with UInt, although there can be a carry but + // we simple remember it in xc and yc (xc and yc can be either 0 or 1), + // and (x0 + x1)*(y0 + y1) we calculate in this way (schoolbook algorithm): + // + // xc | temp + // yc | temp2 + // -------------------- + // (temp * temp2) + // xc*temp2 | + // yc*temp | + // xc*yc | + // ---------- z1 -------- + // + // and the result is never larger in size than 3*first_size + + xc = AddVector(x0, x1, first_size, second_size, temp.table); + yc = AddVector(y0, y1, first_size, second_size, temp2.table); + + Mul3Big2(temp.table, temp2.table, z1.table); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + + // clearing the rest of z1 + for(i=first_size*2 ; i second_size ) + { + uint z1_size = result_size - first_size; + TTMATH_ASSERT( z1_size <= first_size*3 ) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + + for(i=z1_size ; i & ss2) + { + UInt result; + uint i, c = 0; + + MulFastestBig(ss2, result); + + // copying result + for(i=0 ; i & ss2, UInt & result) + { + if( value_size < TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE ) + return Mul2Big(ss2, result); + + uint x1size = value_size, x2size = value_size; + uint x1start = 0, x2start = 0; + + for(x1size=value_size ; x1size>0 && table[x1size-1]==0 ; --x1size); + for(x2size=value_size ; x2size>0 && ss2.table[x2size-1]==0 ; --x2size); + + if( x1size==0 || x2size==0 ) + { + // either 'this' or 'ss2' is equal zero - the result is zero too + result.SetZero(); + return; + } + + for(x1start=0 ; x1start(table, ss2.table, result, x1start, x1size, x2start, x2size); + + + // Karatsuba multiplication + Mul3Big(ss2, result); + + TTMATH_LOG("UInt::MulFastestBig") + } + + + /*! + * + * Division + * + * + */ + +public: + + + /*! + division by one unsigned word + + returns 1 when divisor is zero + */ + uint DivInt(uint divisor, uint * remainder = 0) + { + if( divisor == 0 ) + { + if( remainder ) + *remainder = 0; // this is for convenience, without it the compiler can report that 'remainder' is uninitialized + + TTMATH_LOG("UInt::DivInt") + + return 1; + } + + if( divisor == 1 ) + { + if( remainder ) + *remainder = 0; + + TTMATH_LOG("UInt::DivInt") + + return 0; + } + + UInt dividend(*this); + SetZero(); + + sint i; // i must be with a sign + uint r = 0; + + // we're looking for the last word in ss1 + for(i=value_size-1 ; i>0 && dividend.table[i]==0 ; --i); + + for( ; i>=0 ; --i) + DivTwoWords(r, dividend.table[i], divisor, &table[i], &r); + + if( remainder ) + *remainder = r; + + TTMATH_LOG("UInt::DivInt") + + return 0; + } + + uint DivInt(uint divisor, uint & remainder) + { + return DivInt(divisor, &remainder); + } + + + + /*! + division this = this / ss2 + + return values: + 0 - ok + 1 - division by zero + 'this' will be the quotient + 'remainder' - remainder + */ + uint Div( const UInt & divisor, + UInt * remainder = 0, + uint algorithm = 3) + { + switch( algorithm ) + { + case 1: + return Div1(divisor, remainder); + + case 2: + return Div2(divisor, remainder); + + case 3: + default: + return Div3(divisor, remainder); + } + } + + uint Div(const UInt & divisor, UInt & remainder, uint algorithm = 3) + { + return Div(divisor, &remainder, algorithm); + } + + + +private: + + /*! + return values: + 0 - none has to be done + 1 - division by zero + 2 - division should be made + */ + uint Div_StandardTest( const UInt & v, + uint & m, uint & n, + UInt * remainder = 0) + { + switch( Div_CalculatingSize(v, m, n) ) + { + case 4: // 'this' is equal v + if( remainder ) + remainder->SetZero(); + + SetOne(); + TTMATH_LOG("UInt::Div_StandardTest") + return 0; + + case 3: // 'this' is smaller than v + if( remainder ) + *remainder = *this; + + SetZero(); + TTMATH_LOG("UInt::Div_StandardTest") + return 0; + + case 2: // 'this' is zero + if( remainder ) + remainder->SetZero(); + + SetZero(); + TTMATH_LOG("UInt::Div_StandardTest") + return 0; + + case 1: // v is zero + TTMATH_LOG("UInt::Div_StandardTest") + return 1; + } + + TTMATH_LOG("UInt::Div_StandardTest") + + return 2; + } + + + + /*! + return values: + 0 - ok + 'm' - is the index (from 0) of last non-zero word in table ('this') + 'n' - is the index (from 0) of last non-zero word in v.table + 1 - v is zero + 2 - 'this' is zero + 3 - 'this' is smaller than v + 4 - 'this' is equal v + + if the return value is different than zero the 'm' and 'n' are undefined + */ + uint Div_CalculatingSize(const UInt & v, uint & m, uint & n) + { + m = n = value_size-1; + + for( ; n!=0 && v.table[n]==0 ; --n); + + if( n==0 && v.table[n]==0 ) + return 1; + + for( ; m!=0 && table[m]==0 ; --m); + + if( m==0 && table[m]==0 ) + return 2; + + if( m < n ) + return 3; + else + if( m == n ) + { + uint i; + for(i = n ; i!=0 && table[i]==v.table[i] ; --i); + + if( table[i] < v.table[i] ) + return 3; + else + if (table[i] == v.table[i] ) + return 4; + } + + return 0; + } + + +public: + + /*! + the first division algorithm + radix 2 + */ + uint Div1(const UInt & divisor, UInt * remainder = 0) + { + uint m,n, test; + + test = Div_StandardTest(divisor, m, n, remainder); + if( test < 2 ) + return test; + + if( !remainder ) + { + UInt rem; + + return Div1_Calculate(divisor, rem); + } + + return Div1_Calculate(divisor, *remainder); + } + + + /*! + the first division algorithm + radix 2 + */ + uint Div1(const UInt & divisor, UInt & remainder) + { + return Div1(divisor, &remainder); + } + + +private: + + uint Div1_Calculate(const UInt & divisor, UInt & rest) + { + if( this == &divisor ) + { + UInt divisor_copy(divisor); + return Div1_CalculateRef(divisor_copy, rest); + } + else + { + return Div1_CalculateRef(divisor, rest); + } + } + + + uint Div1_CalculateRef(const UInt & divisor, UInt & rest) + { + TTMATH_REFERENCE_ASSERT( divisor ) + + sint loop; + sint c; + + rest.SetZero(); + loop = value_size * TTMATH_BITS_PER_UINT; + c = 0; + + + div_a: + c = Rcl(1, c); + c = rest.Add(rest,c); + c = rest.Sub(divisor,c); + + c = !c; + + if(!c) + goto div_d; + + + div_b: + --loop; + if(loop) + goto div_a; + + c = Rcl(1, c); + TTMATH_LOG("UInt::Div1_Calculate") + return 0; + + + div_c: + c = Rcl(1, c); + c = rest.Add(rest,c); + c = rest.Add(divisor); + + if(c) + goto div_b; + + + div_d: + --loop; + if(loop) + goto div_c; + + c = Rcl(1, c); + c = rest.Add(divisor); + + TTMATH_LOG("UInt::Div1_Calculate") + + return 0; + } + + +public: + + /*! + the second division algorithm + + return values: + 0 - ok + 1 - division by zero + */ + uint Div2(const UInt & divisor, UInt * remainder = 0) + { + if( this == &divisor ) + { + UInt divisor_copy(divisor); + return Div2Ref(divisor_copy, remainder); + } + else + { + return Div2Ref(divisor, remainder); + } + } + + + /*! + the second division algorithm + + return values: + 0 - ok + 1 - division by zero + */ + uint Div2(const UInt & divisor, UInt & remainder) + { + return Div2(divisor, &remainder); + } + + +private: + + /*! + the second division algorithm + + return values: + 0 - ok + 1 - division by zero + */ + uint Div2Ref(const UInt & divisor, UInt * remainder = 0) + { + uint bits_diff; + uint status = Div2_Calculate(divisor, remainder, bits_diff); + if( status < 2 ) + return status; + + if( CmpBiggerEqual(divisor) ) + { + Div2(divisor, remainder); + SetBit(bits_diff); + } + else + { + if( remainder ) + *remainder = *this; + + SetZero(); + SetBit(bits_diff); + } + + TTMATH_LOG("UInt::Div2") + + return 0; + } + + + /*! + return values: + 0 - we've calculated the division + 1 - division by zero + 2 - we have to still calculate + + */ + uint Div2_Calculate(const UInt & divisor, UInt * remainder, + uint & bits_diff) + { + uint table_id, index; + uint divisor_table_id, divisor_index; + + uint status = Div2_FindLeadingBitsAndCheck( divisor, remainder, + table_id, index, + divisor_table_id, divisor_index); + + if( status < 2 ) + { + TTMATH_LOG("UInt::Div2_Calculate") + return status; + } + + // here we know that 'this' is greater than divisor + // then 'index' is greater or equal 'divisor_index' + bits_diff = index - divisor_index; + + UInt divisor_copy(divisor); + divisor_copy.Rcl(bits_diff, 0); + + if( CmpSmaller(divisor_copy, table_id) ) + { + divisor_copy.Rcr(1); + --bits_diff; + } + + Sub(divisor_copy, 0); + + TTMATH_LOG("UInt::Div2_Calculate") + + return 2; + } + + + /*! + return values: + 0 - we've calculated the division + 1 - division by zero + 2 - we have to still calculate + */ + uint Div2_FindLeadingBitsAndCheck( const UInt & divisor, + UInt * remainder, + uint & table_id, uint & index, + uint & divisor_table_id, uint & divisor_index) + { + if( !divisor.FindLeadingBit(divisor_table_id, divisor_index) ) + { + // division by zero + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + return 1; + } + + if( !FindLeadingBit(table_id, index) ) + { + // zero is divided by something + + SetZero(); + + if( remainder ) + remainder->SetZero(); + + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + + return 0; + } + + divisor_index += divisor_table_id * TTMATH_BITS_PER_UINT; + index += table_id * TTMATH_BITS_PER_UINT; + + if( divisor_table_id == 0 ) + { + // dividor has only one 32-bit word + + uint r; + DivInt(divisor.table[0], &r); + + if( remainder ) + { + remainder->SetZero(); + remainder->table[0] = r; + } + + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + + return 0; + } + + + if( Div2_DivisorGreaterOrEqual( divisor, remainder, + table_id, index, + divisor_index) ) + { + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + return 0; + } + + + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + + return 2; + } + + + /*! + return values: + true if divisor is equal or greater than 'this' + */ + bool Div2_DivisorGreaterOrEqual( const UInt & divisor, + UInt * remainder, + uint table_id, uint index, + uint divisor_index ) + { + if( divisor_index > index ) + { + // divisor is greater than this + + if( remainder ) + *remainder = *this; + + SetZero(); + + TTMATH_LOG("UInt::Div2_DivisorGreaterOrEqual") + + return true; + } + + if( divisor_index == index ) + { + // table_id == divisor_table_id as well + + uint i; + for(i = table_id ; i!=0 && table[i]==divisor.table[i] ; --i); + + if( table[i] < divisor.table[i] ) + { + // divisor is greater than 'this' + + if( remainder ) + *remainder = *this; + + SetZero(); + + TTMATH_LOG("UInt::Div2_DivisorGreaterOrEqual") + + return true; + } + else + if( table[i] == divisor.table[i] ) + { + // divisor is equal 'this' + + if( remainder ) + remainder->SetZero(); + + SetOne(); + + TTMATH_LOG("UInt::Div2_DivisorGreaterOrEqual") + + return true; + } + } + + TTMATH_LOG("UInt::Div2_DivisorGreaterOrEqual") + + return false; + } + + +public: + + /*! + the third division algorithm + */ + uint Div3(const UInt & ss2, UInt * remainder = 0) + { + if( this == &ss2 ) + { + UInt copy_ss2(ss2); + return Div3Ref(copy_ss2, remainder); + } + else + { + return Div3Ref(ss2, remainder); + } + } + + + /*! + the third division algorithm + */ + uint Div3(const UInt & ss2, UInt & remainder) + { + return Div3(ss2, &remainder); + } + + +private: + + /*! + the third division algorithm + + this algorithm is described in the following book: + "The art of computer programming 2" (4.3.1 page 272) + Donald E. Knuth + !! give the description here (from the book) + */ + uint Div3Ref(const UInt & v, UInt * remainder = 0) + { + uint m,n, test; + + test = Div_StandardTest(v, m, n, remainder); + if( test < 2 ) + return test; + + if( n == 0 ) + { + uint r; + DivInt( v.table[0], &r ); + + if( remainder ) + { + remainder->SetZero(); + remainder->table[0] = r; + } + + TTMATH_LOG("UInt::Div3") + + return 0; + } + + + // we can only use the third division algorithm when + // the divisor is greater or equal 2^32 (has more than one 32-bit word) + ++m; + ++n; + m = m - n; + Div3_Division(v, remainder, m, n); + + TTMATH_LOG("UInt::Div3") + + return 0; + } + + + +private: + + + void Div3_Division(UInt v, UInt * remainder, uint m, uint n) + { + TTMATH_ASSERT( n>=2 ) + + UInt uu, vv; + UInt q; + uint d, u_value_size, u0, u1, u2, v1, v0, j=m; + + u_value_size = Div3_Normalize(v, n, d); + + if( j+n == value_size ) + u2 = u_value_size; + else + u2 = table[j+n]; + + Div3_MakeBiggerV(v, vv); + + for(uint i = j+1 ; i & uu, uint j, uint n, uint u_max) + { + uint i; + + for(i=0 ; i so and 'i' is from <0..value_size> + // then table[i] is always correct (look at the declaration of 'uu') + uu.table[i] = u_max; + + for( ++i ; i & uu, uint j, uint n) + { + uint i; + + for(i=0 ; i & v, UInt & vv) + { + for(uint i=0 ; i & v, uint n, uint & d) + { + // v.table[n-1] is != 0 + + uint bit = (uint)FindLeadingBitInWord(v.table[n-1]); + uint move = (TTMATH_BITS_PER_UINT - bit - 1); + uint res = table[value_size-1]; + d = move; + + if( move > 0 ) + { + v.Rcl(move, 0); + Rcl(move, 0); + res = res >> (bit + 1); + } + else + { + res = 0; + } + + TTMATH_LOG("UInt::Div3_Normalize") + + return res; + } + + + void Div3_Unnormalize(UInt * remainder, uint n, uint d) + { + for(uint i=n ; i u_temp; + uint rp; + bool next_test; + + TTMATH_ASSERT( v1 != 0 ) + + u_temp.table[1] = u2; + u_temp.table[0] = u1; + u_temp.DivInt(v1, &rp); + + TTMATH_ASSERT( u_temp.table[1]==0 || u_temp.table[1]==1 ) + + do + { + bool decrease = false; + + if( u_temp.table[1] == 1 ) + decrease = true; + else + { + UInt<2> temp1, temp2; + + UInt<2>::MulTwoWords(u_temp.table[0], v0, temp1.table+1, temp1.table); + temp2.table[1] = rp; + temp2.table[0] = u0; + + if( temp1 > temp2 ) + decrease = true; + } + + next_test = false; + + if( decrease ) + { + u_temp.SubOne(); + + rp += v1; + + if( rp >= v1 ) // it means that there wasn't a carry (r & uu, + const UInt & vv, uint & qp) + { + // D4 (in the book) + + UInt vv_temp(vv); + vv_temp.MulInt(qp); + + if( uu.Sub(vv_temp) ) + { + // there was a carry + + // + // !!! this part of code was not tested + // + + --qp; + uu.Add(vv); + + // can be a carry from this additions but it should be ignored + // because it cancels with the borrow from uu.Sub(vv_temp) + } + + TTMATH_LOG("UInt::Div3_MultiplySubtract") + } + + + + + + +public: + + + /*! + power this = this ^ pow + binary algorithm (r-to-l) + + return values: + 0 - ok + 1 - carry + 2 - incorrect argument (0^0) + */ + uint Pow(UInt pow) + { + if(pow.IsZero() && IsZero()) + // we don't define zero^zero + return 2; + + UInt start(*this); + UInt result; + result.SetOne(); + uint c = 0; + + while( !c ) + { + if( pow.table[0] & 1 ) + c += result.Mul(start); + + pow.Rcr2_one(0); + if( pow.IsZero() ) + break; + + c += start.Mul(start); + } + + *this = result; + + TTMATH_LOGC("UInt::Pow(UInt<>)", c) + + return (c==0)? 0 : 1; + } + + + /*! + square root + e.g. Sqrt(9) = 3 + ('digit-by-digit' algorithm) + */ + void Sqrt() + { + UInt bit, temp; + + if( IsZero() ) + return; + + UInt value(*this); + + SetZero(); + bit.SetZero(); + bit.table[value_size-1] = (TTMATH_UINT_HIGHEST_BIT >> 1); + + while( bit > value ) + bit.Rcr(2); + + while( !bit.IsZero() ) + { + temp = *this; + temp.Add(bit); + + if( value >= temp ) + { + value.Sub(temp); + Rcr(1); + Add(bit); + } + else + { + Rcr(1); + } + + bit.Rcr(2); + } + + TTMATH_LOG("UInt::Sqrt") + } + + + + /*! + this method sets n first bits to value zero + + For example: + let n=2 then if there's a value 111 (bin) there'll be '100' (bin) + */ + void ClearFirstBits(uint n) + { + if( n >= value_size*TTMATH_BITS_PER_UINT ) + { + SetZero(); + TTMATH_LOG("UInt::ClearFirstBits") + return; + } + + uint * p = table; + + // first we're clearing the whole words + while( n >= TTMATH_BITS_PER_UINT ) + { + *p++ = 0; + n -= TTMATH_BITS_PER_UINT; + } + + if( n == 0 ) + { + TTMATH_LOG("UInt::ClearFirstBits") + return; + } + + // and then we're clearing one word which has left + // mask -- all bits are set to one + uint mask = TTMATH_UINT_MAX_VALUE; + + mask = mask << n; + + (*p) &= mask; + + TTMATH_LOG("UInt::ClearFirstBits") + } + + + /*! + this method returns true if the highest bit of the value is set + */ + bool IsTheHighestBitSet() const + { + return (table[value_size-1] & TTMATH_UINT_HIGHEST_BIT) != 0; + } + + + /*! + this method returns true if the lowest bit of the value is set + */ + bool IsTheLowestBitSet() const + { + return (*table & 1) != 0; + } + + + /*! + returning true if only the highest bit is set + */ + bool IsOnlyTheHighestBitSet() const + { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + + for(uint i=0 ; i> (TTMATH_BITS_PER_UINT - rest); + + return (table[i] & mask) == 0; + } + + + + /*! + * + * conversion methods + * + */ + + + + /*! + this method converts an UInt type to this class + + this operation has mainly sense if the value from p is + equal or smaller than that one which is returned from UInt::SetMax() + + it returns a carry if the value 'p' is too big + */ + template + uint FromUInt(const UInt & p) + { + uint min_size = (value_size < argument_size)? value_size : argument_size; + uint i; + + for(i=0 ; i argument_size ) + { + // 'this' is longer than 'p' + + for( ; i)", 1) + return 1; + } + } + + TTMATH_LOGC("UInt::FromUInt(UInt<>)", 0) + + return 0; + } + + + /*! + this method converts an UInt type to this class + + this operation has mainly sense if the value from p is + equal or smaller than that one which is returned from UInt::SetMax() + + it returns a carry if the value 'p' is too big + */ + template + uint FromInt(const UInt & p) + { + return FromUInt(p); + } + + + /*! + this method converts the uint type to this class + */ + uint FromUInt(uint value) + { + for(uint i=1 ; i type to this class + + it doesn't return a carry + */ +/* template + UInt & operator=(const UInt & p) + { + FromUInt(p); + + return *this; + } +*/ + + /*! + the assignment operator + */ +/* UInt & operator=(const UInt & p) + { + for(uint i=0 ; i)") + + return *this; + } +*/ + + /*! + this method converts the uint type to this class + */ + UInt & operator=(uint i) + { + FromUInt(i); + + return *this; + } + + + /*! + a constructor for converting the uint to this class + */ +/* UInt(uint i) + { + FromUInt(i); + } +*/ + + /*! + this method converts the sint type to this class + */ + UInt & operator=(sint i) + { + FromInt(i); + + return *this; + } + + + /*! + a constructor for converting the sint to this class + + look at the description of UInt::operator=(sint) + */ +/* UInt(sint i) + { + FromInt(i); + } +*/ + +#ifdef TTMATH_PLATFORM32 + + + /*! + this method converts unsigned 64 bit int type to this class + ***this method is created only on a 32bit platform*** + */ + uint FromUInt(ulint n) + { + table[0] = (uint)n; + + if( value_size == 1 ) + { + uint c = ((n >> TTMATH_BITS_PER_UINT) == 0) ? 0 : 1; + + TTMATH_LOGC("UInt::FromUInt(ulint)", c) + return c; + } + + table[1] = (uint)(n >> TTMATH_BITS_PER_UINT); + + for(uint i=2 ; i & operator=(ulint n) + { + FromUInt(n); + + return *this; + } + + + /*! + a constructor for converting unsigned 64 bit int to this class + ***this constructor is created only on a 32bit platform*** + */ +/* UInt(ulint n) + { + FromUInt(n); + } +*/ + + /*! + this operator converts signed 64 bit int type to this class + ***this operator is created only on a 32bit platform*** + */ + UInt & operator=(slint n) + { + FromInt(n); + + return *this; + } + + + /*! + a constructor for converting signed 64 bit int to this class + ***this constructor is created only on a 32bit platform*** + */ +/* UInt(slint n) + { + FromInt(n); + } +*/ +#endif + + + +#ifdef TTMATH_PLATFORM64 + + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromUInt(unsigned int i) + { + return FromUInt(uint(i)); + } + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromInt(unsigned int i) + { + return FromUInt(uint(i)); + } + + + /*! + this method converts 32 bit signed int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromInt(signed int i) + { + return FromInt(sint(i)); + } + + + /*! + this operator converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + UInt & operator=(unsigned int i) + { + FromUInt(i); + + return *this; + } + + + /*! + a constructor for converting 32 bit unsigned int to this class + ***this constructor is created only on a 64bit platform*** + */ +/* UInt(unsigned int i) + { + FromUInt(i); + } +*/ + + /*! + an operator for converting 32 bit signed int to this class + ***this constructor is created only on a 64bit platform*** + */ + UInt & operator=(signed int i) + { + FromInt(i); + + return *this; + } + + + /*! + a constructor for converting 32 bit signed int to this class + ***this constructor is created only on a 64bit platform*** + */ +/* UInt(signed int i) + { + FromInt(i); + } +*/ + +#endif + + + + + + /*! + a constructor for converting a string to this class (with the base=10) + */ +/* UInt(const char * s) + { + FromString(s); + } +*/ + + /*! + a constructor for converting a string to this class (with the base=10) + */ +/* UInt(const std::string & s) + { + FromString( s.c_str() ); + } +*/ + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + a constructor for converting a string to this class (with the base=10) + */ + UInt(const wchar_t * s) + { + FromString(s); + } + + + /*! + a constructor for converting a string to this class (with the base=10) + */ + UInt(const std::wstring & s) + { + FromString( s.c_str() ); + } + +#endif + + + + + /*! + a default constructor + + we don't clear the table + */ +/* UInt() + { + // when macro TTMATH_DEBUG_LOG is defined + // we set special values to the table + // in order to be everywhere the same value of the UInt object + // without this it would be difficult to analyse the log file + #ifdef TTMATH_DEBUG_LOG + #ifdef TTMATH_PLATFORM32 + for(uint i=0 ; i & u) + { + for(uint i=0 ; i)") + } +*/ + + + /*! + a template for producting constructors for copying from another types + */ +/* template + UInt(const UInt & u) + { + // look that 'size' we still set as 'value_size' and not as u.value_size + FromUInt(u); + } +*/ + + + + /*! + a destructor + */ +/* ~UInt() + { + } +*/ + + /*! + this method returns the lowest value from table + + we must be sure when we using this method whether the value + will hold in an uint type or not (the rest value from the table must be zero) + */ + uint ToUInt() const + { + return table[0]; + } + + + /*! + this method converts the value to uint type + can return a carry if the value is too long to store it in uint type + */ + uint ToUInt(uint & result) const + { + result = table[0]; + + for(uint i=1 ; i> 32) != 0 ) + return 1; + + for(uint i=1 ; i + */ + double ToStringLog2(uint x) const + { + static double log_tab[] = { + 1.000000000000000000, + 0.630929753571457437, + 0.500000000000000000, + 0.430676558073393050, + 0.386852807234541586, + 0.356207187108022176, + 0.333333333333333333, + 0.315464876785728718, + 0.301029995663981195, + 0.289064826317887859, + 0.278942945651129843, + 0.270238154427319741, + 0.262649535037193547, + 0.255958024809815489, + 0.250000000000000000 + }; + + if( x<2 || x>16 ) + return 0; + + return log_tab[x-2]; + } + + +public: + + + /*! + an auxiliary method for converting to a string + it's used from Int::ToString() too (negative is set true then) + */ + template + void ToStringBase(string_type & result, uint b = 10, bool negative = false) const + { + UInt temp(*this); + uint rest, table_id, index, digits; + double digits_d; + char character; + + result.clear(); + + if( b<2 || b>16 ) + return; + + if( !FindLeadingBit(table_id, index) ) + { + result = '0'; + return; + } + + if( negative ) + result = '-'; + + digits_d = table_id; // for not making an overflow in uint type + digits_d *= TTMATH_BITS_PER_UINT; + digits_d += index + 1; + digits_d *= ToStringLog2(b); + digits = static_cast(digits_d) + 3; // plus some epsilon + + if( result.capacity() < digits ) + result.reserve(digits); + + do + { + temp.DivInt(b, &rest); + character = static_cast(Misc::DigitToChar(rest)); + result.insert(result.end(), character); + } + while( !temp.IsZero() ); + + size_t i1 = negative ? 1 : 0; // the first is a hyphen (when negative is true) + size_t i2 = result.size() - 1; + + for( ; i1 < i2 ; ++i1, --i2 ) + { + char tempc = static_cast(result[i1]); + result[i1] = result[i2]; + result[i2] = tempc; + } + } + + + + /*! + this method converts the value to a string with a base equal 'b' + */ + void ToString(std::string & result, uint b = 10) const + { + return ToStringBase(result, b); + } + + + std::string ToString(uint b = 10) const + { + std::string result; + ToStringBase(result, b); + + return result; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + void ToString(std::wstring & result, uint b = 10) const + { + return ToStringBase(result, b); + } + + std::wstring ToWString(uint b = 10) const + { + std::wstring result; + ToStringBase(result, b); + + return result; + } + +#endif + + + +private: + + /*! + an auxiliary method for converting from a string + */ + template + uint FromStringBase(const char_type * s, uint b = 10, const char_type ** after_source = 0, bool * value_read = 0) + { + UInt base; + base.FromUInt( b ); + UInt temp; + sint z; + uint c = 0; + + SetZero(); + temp.SetZero(); + Misc::SkipWhiteCharacters(s); + + if( after_source ) + *after_source = s; + + if( value_read ) + *value_read = false; + + if( b<2 || b>16 ) + return 1; + + + for( ; (z=Misc::CharToDigit(*s, b)) != -1 ; ++s) + { + if( value_read ) + *value_read = true; + + if( c == 0 ) + { + temp.table[0] = z; + + c += Mul(base); // !! IMPROVE ME: there can be used MulInt here + c += Add(temp); + } + } + + if( after_source ) + *after_source = s; + + TTMATH_LOGC("UInt::FromString", c) + + return (c==0)? 0 : 1; + } + + +public: + + + /*! + this method converts a string into its value + it returns carry=1 if the value will be too big or an incorrect base 'b' is given + + string is ended with a non-digit value, for example: + "12" will be translated to 12 + as well as: + "12foo" will be translated to 12 too + + existing first white characters will be ommited + + if the value from s is too large the rest digits will be skipped + + after_source (if exists) is pointing at the end of the parsed string + + value_read (if exists) tells whether something has actually been read (at least one digit) + */ + uint FromString(const char * s, uint b = 10, const char ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(s, b, after_source, value_read); + } + + + /*! + this method converts a string into its value + + (it returns carry=1 if the value will be too big or an incorrect base 'b' is given) + */ + uint FromString(const std::string & s, uint b = 10) + { + return FromString( s.c_str(), b ); + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + UInt & operator=(const char * s) + { + FromString(s); + + return *this; + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + UInt & operator=(const std::string & s) + { + FromString( s.c_str() ); + + return *this; + } + + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method converts a string into its value + */ + uint FromString(const wchar_t * s, uint b = 10, const wchar_t ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(s, b, after_source, value_read); + } + + + /*! + this method converts a string into its value + + (it returns carry=1 if the value will be too big or an incorrect base 'b' is given) + */ + uint FromString(const std::wstring & s, uint b = 10) + { + return FromString( s.c_str(), b ); + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + UInt & operator=(const wchar_t * s) + { + FromString(s); + + return *this; + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + UInt & operator=(const std::wstring & s) + { + FromString( s.c_str() ); + + return *this; + } + +#endif + + + /*! + * + * methods for comparing + * + */ + + + /*! + this method returns true if 'this' is smaller than 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + I introduced it for some kind of optimization made in the second division algorithm (Div2) + */ + bool CmpSmaller(const UInt & l, sint index = -1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + { + if( table[i] != l.table[i] ) + return table[i] < l.table[i]; + } + + // they're equal + return false; + } + + + + /*! + this method returns true if 'this' is bigger than 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + + I introduced it for some kind of optimization made in the second division algorithm (Div2) + */ + bool CmpBigger(const UInt & l, sint index = -1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + { + if( table[i] != l.table[i] ) + return table[i] > l.table[i]; + } + + // they're equal + return false; + } + + + /*! + this method returns true if 'this' is equal 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + */ + bool CmpEqual(const UInt & l, sint index = -1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + if( table[i] != l.table[i] ) + return false; + + return true; + } + + + + /*! + this method returns true if 'this' is smaller than or equal 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + */ + bool CmpSmallerEqual(const UInt & l, sint index=-1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + { + if( table[i] != l.table[i] ) + return table[i] < l.table[i]; + } + + // they're equal + return true; + } + + + + /*! + this method returns true if 'this' is bigger than or equal 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + */ + bool CmpBiggerEqual(const UInt & l, sint index=-1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + { + if( table[i] != l.table[i] ) + return table[i] > l.table[i]; + } + + // they're equal + return true; + } + + + /* + operators for comparising + */ + + bool operator<(const UInt & l) const + { + return CmpSmaller(l); + } + + + bool operator>(const UInt & l) const + { + return CmpBigger(l); + } + + + bool operator==(const UInt & l) const + { + return CmpEqual(l); + } + + + bool operator!=(const UInt & l) const + { + return !operator==(l); + } + + + bool operator<=(const UInt & l) const + { + return CmpSmallerEqual(l); + } + + bool operator>=(const UInt & l) const + { + return CmpBiggerEqual(l); + } + + + /*! + * + * standard mathematical operators + * + */ + + UInt operator-(const UInt & p2) const + { + UInt temp(*this); + + temp.Sub(p2); + + return temp; + } + + UInt & operator-=(const UInt & p2) + { + Sub(p2); + + return *this; + } + + UInt operator+(const UInt & p2) const + { + UInt temp(*this); + + temp.Add(p2); + + return temp; + } + + UInt & operator+=(const UInt & p2) + { + Add(p2); + + return *this; + } + + + UInt operator*(const UInt & p2) const + { + UInt temp(*this); + + temp.Mul(p2); + + return temp; + } + + + UInt & operator*=(const UInt & p2) + { + Mul(p2); + + return *this; + } + + + UInt operator/(const UInt & p2) const + { + UInt temp(*this); + + temp.Div(p2); + + return temp; + } + + + UInt & operator/=(const UInt & p2) + { + Div(p2); + + return *this; + } + + + UInt operator%(const UInt & p2) const + { + UInt temp(*this); + UInt remainder; + + temp.Div( p2, remainder ); + + return remainder; + } + + + UInt & operator%=(const UInt & p2) + { + UInt remainder; + + Div( p2, remainder ); + operator=(remainder); + + return *this; + } + + + /*! + Prefix operator e.g ++variable + */ + UInt & operator++() + { + AddOne(); + + return *this; + } + + + /*! + Postfix operator e.g variable++ + */ + UInt operator++(int) + { + UInt temp( *this ); + + AddOne(); + + return temp; + } + + + UInt & operator--() + { + SubOne(); + + return *this; + } + + + UInt operator--(int) + { + UInt temp( *this ); + + SubOne(); + + return temp; + } + + + + /*! + * + * bitwise operators + * + */ + + UInt operator~() const + { + UInt temp( *this ); + + temp.BitNot(); + + return temp; + } + + + UInt operator&(const UInt & p2) const + { + UInt temp( *this ); + + temp.BitAnd(p2); + + return temp; + } + + + UInt & operator&=(const UInt & p2) + { + BitAnd(p2); + + return *this; + } + + + UInt operator|(const UInt & p2) const + { + UInt temp( *this ); + + temp.BitOr(p2); + + return temp; + } + + + UInt & operator|=(const UInt & p2) + { + BitOr(p2); + + return *this; + } + + + UInt operator^(const UInt & p2) const + { + UInt temp( *this ); + + temp.BitXor(p2); + + return temp; + } + + + UInt & operator^=(const UInt & p2) + { + BitXor(p2); + + return *this; + } + + + UInt operator>>(int move) const + { + UInt temp( *this ); + + temp.Rcr(move); + + return temp; + } + + + UInt & operator>>=(int move) + { + Rcr(move); + + return *this; + } + + + UInt operator<<(int move) const + { + UInt temp( *this ); + + temp.Rcl(move); + + return temp; + } + + + UInt & operator<<=(int move) + { + Rcl(move); + + return *this; + } + + + /*! + * + * input/output operators for standard streams + * + * (they are very simple, in the future they should be changed) + * + */ + + +private: + + + /*! + an auxiliary method for outputing to standard streams + */ + template + static ostream_type & OutputToStream(ostream_type & s, const UInt & l) + { + string_type ss; + + l.ToString(ss); + s << ss; + + return s; + } + + +public: + + + /*! + output to standard streams + */ + friend std::ostream & operator<<(std::ostream & s, const UInt & l) + { + return OutputToStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + output to standard streams + */ + friend std::wostream & operator<<(std::wostream & s, const UInt & l) + { + return OutputToStream(s, l); + } + +#endif + + + +private: + + /*! + an auxiliary method for reading from standard streams + */ + template + static istream_type & InputFromStream(istream_type & s, UInt & l) + { + string_type ss; + + // char or wchar_t for operator>> + char_type z; + + // operator>> omits white characters if they're set for ommiting + s >> z; + + // we're reading only digits (base=10) + while( s.good() && Misc::CharToDigit(z, 10)>=0 ) + { + ss += z; + z = static_cast(s.get()); + } + + // we're leaving the last read character + // (it's not belonging to the value) + s.unget(); + + l.FromString(ss); + + return s; + } + +public: + + + /*! + input from standard streams + */ + friend std::istream & operator>>(std::istream & s, UInt & l) + { + return InputFromStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + input from standard streams + */ + friend std::wistream & operator>>(std::wistream & s, UInt & l) + { + return InputFromStream(s, l); + } + +#endif + + + /* + Following methods are defined in: + ttmathuint_x86.h + ttmathuint_x86_64.h + ttmathuint_noasm.h + */ + +#ifdef TTMATH_NOASM + static uint AddTwoWords(uint a, uint b, uint carry, uint * result); + static uint SubTwoWords(uint a, uint b, uint carry, uint * result); + +#ifdef TTMATH_PLATFORM64 + + union uint_ + { + struct + { + unsigned int low; // 32 bit + unsigned int high; // 32 bit + } u_; + + uint u; // 64 bit + }; + + + static void DivTwoWords2(uint a,uint b, uint c, uint * r, uint * rest); + static uint DivTwoWordsNormalize(uint_ & a_, uint_ & b_, uint_ & c_); + static uint DivTwoWordsUnnormalize(uint u, uint d); + static unsigned int DivTwoWordsCalculate(uint_ u_, unsigned int u3, uint_ v_); + static void MultiplySubtract(uint_ & u_, unsigned int & u3, unsigned int & q, uint_ v_); + +#endif // TTMATH_PLATFORM64 +#endif // TTMATH_NOASM + + +private: + uint Rcl2_one(uint c); + uint Rcr2_one(uint c); + uint Rcl2(uint bits, uint c); + uint Rcr2(uint bits, uint c); + +public: + static const char * LibTypeStr(); + static LibTypeCode LibType(); + uint Add(const UInt & ss2, uint c=0); + uint AddInt(uint value, uint index = 0); + uint AddTwoInts(uint x2, uint x1, uint index); + static uint AddVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result); + uint Sub(const UInt & ss2, uint c=0); + uint SubInt(uint value, uint index = 0); + static uint SubVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result); + static sint FindLeadingBitInWord(uint x); + static sint FindLowestBitInWord(uint x); + static uint SetBitInWord(uint & value, uint bit); + static void MulTwoWords(uint a, uint b, uint * result_high, uint * result_low); + static void DivTwoWords(uint a,uint b, uint c, uint * r, uint * rest); + +}; + + + +/*! + this specialization is needed in order to not confused the compiler "error: ISO C++ forbids zero-size array" + when compiling Mul3Big2() method +*/ +template<> +class UInt<0> +{ +public: + uint table[1]; + + void Mul2Big(const UInt<0> &, UInt<0> &) { TTMATH_ASSERT(false) }; + void SetZero() { TTMATH_ASSERT(false) }; + uint AddTwoInts(uint, uint, uint) { TTMATH_ASSERT(false) return 0; }; +}; + + +} //namespace + + +#include "ttmathuint_x86.h" +#include "ttmathuint_x86_64.h" +#include "ttmathuint_noasm.h" + +#endif diff --git a/extern/ttmath/ttmathuint_noasm.h b/extern/ttmath/ttmathuint_noasm.h new file mode 100644 index 0000000000..07c73fc499 --- /dev/null +++ b/extern/ttmath/ttmathuint_noasm.h @@ -0,0 +1,1017 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2010, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef headerfilettmathuint_noasm +#define headerfilettmathuint_noasm + + +#ifdef TTMATH_NOASM + +/*! + \file ttmathuint_noasm.h + \brief template class UInt with methods without any assembler code + + this file is included at the end of ttmathuint.h +*/ + + +namespace ttmath +{ + + /*! + returning the string represents the currect type of the library + we have following types: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + */ + template + const char * UInt::LibTypeStr() + { + #ifdef TTMATH_PLATFORM32 + static const char info[] = "no_asm_32"; + #endif + + #ifdef TTMATH_PLATFORM64 + static const char info[] = "no_asm_64"; + #endif + + return info; + } + + + /*! + returning the currect type of the library + */ + template + LibTypeCode UInt::LibType() + { + #ifdef TTMATH_PLATFORM32 + LibTypeCode info = no_asm_32; + #endif + + #ifdef TTMATH_PLATFORM64 + LibTypeCode info = no_asm_64; + #endif + + return info; + } + + + /*! + this method adds two words together + returns carry + + this method is created only when TTMATH_NOASM macro is defined + */ + template + uint UInt::AddTwoWords(uint a, uint b, uint carry, uint * result) + { + uint temp; + + if( carry == 0 ) + { + temp = a + b; + + if( temp < a ) + carry = 1; + } + else + { + carry = 1; + temp = a + b + carry; + + if( temp > a ) // !(temp<=a) + carry = 0; + } + + *result = temp; + + return carry; + } + + + + /*! + this method adding ss2 to the this and adding carry if it's defined + (this = this + ss2 + c) + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it was) + */ + + template + uint UInt::Add(const UInt & ss2, uint c) + { + uint i; + + for(i=0 ; i + uint UInt::AddInt(uint value, uint index) + { + uint i, c; + + TTMATH_ASSERT( index < value_size ) + + + c = AddTwoWords(table[index], value, 0, &table[index]); + + for(i=index+1 ; i + uint UInt::AddTwoInts(uint x2, uint x1, uint index) + { + uint i, c; + + TTMATH_ASSERT( index < value_size - 1 ) + + + c = AddTwoWords(table[index], x1, 0, &table[index]); + c = AddTwoWords(table[index+1], x2, c, &table[index+1]); + + for(i=index+2 ; i + uint UInt::AddVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + uint i, c = 0; + + TTMATH_ASSERT( ss1_size >= ss2_size ) + + for(i=0 ; i + uint UInt::SubTwoWords(uint a, uint b, uint carry, uint * result) + { + if( carry == 0 ) + { + *result = a - b; + + if( a < b ) + carry = 1; + } + else + { + carry = 1; + *result = a - b - carry; + + if( a > b ) // !(a <= b ) + carry = 0; + } + + return carry; + } + + + + + /*! + this method's subtracting ss2 from the 'this' and subtracting + carry if it has been defined + (this = this - ss2 - c) + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it was) + */ + template + uint UInt::Sub(const UInt & ss2, uint c) + { + uint i; + + for(i=0 ; i + uint UInt::SubInt(uint value, uint index) + { + uint i, c; + + TTMATH_ASSERT( index < value_size ) + + + c = SubTwoWords(table[index], value, 0, &table[index]); + + for(i=index+1 ; i + uint UInt::SubVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + uint i, c = 0; + + TTMATH_ASSERT( ss1_size >= ss2_size ) + + for(i=0 ; i + uint UInt::Rcl2_one(uint c) + { + uint i, new_c; + + if( c != 0 ) + c = 1; + + for(i=0 ; i this -> return value + + the highest *bit* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2_one(1) there'll be 100000001 and Rcr2_one returns 0 + */ + template + uint UInt::Rcr2_one(uint c) + { + sint i; // signed i + uint new_c; + + if( c != 0 ) + c = TTMATH_UINT_HIGHEST_BIT; + + for(i=sint(value_size)-1 ; i>=0 ; --i) + { + new_c = (table[i] & 1) ? TTMATH_UINT_HIGHEST_BIT : 0; + table[i] = (table[i] >> 1) | c; + c = new_c; + } + + c = (c != 0)? 1 : 0; + + TTMATH_LOGC("UInt::Rcr2_one", c) + + return c; + } + + + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bits* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2(3, 1) there'll be 010000111 and Rcl2 returns 1 + */ + template + uint UInt::Rcl2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits> move; + + for(i=0 ; i> move; + table[i] = (table[i] << bits) | c; + c = new_c; + } + + TTMATH_LOGC("UInt::Rcl2", (c & 1)) + + return (c & 1); + } + + + + + /*! + this method moves all bits into the right hand side + C -> this -> return value + + the highest *bits* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2(2, 1) there'll be 110000000 and Rcr2 returns 1 + */ + template + uint UInt::Rcr2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits=0 ; --i) + { + new_c = table[i] << move; + table[i] = (table[i] >> bits) | c; + c = new_c; + } + + c = (c & TTMATH_UINT_HIGHEST_BIT) ? 1 : 0; + + TTMATH_LOGC("UInt::Rcr2", c) + + return c; + } + + + + + /*! + this method returns the number of the highest set bit in x + if the 'x' is zero this method returns '-1' + */ + template + sint UInt::FindLeadingBitInWord(uint x) + { + if( x == 0 ) + return -1; + + uint bit = TTMATH_BITS_PER_UINT - 1; + + while( (x & TTMATH_UINT_HIGHEST_BIT) == 0 ) + { + x = x << 1; + --bit; + } + + return bit; + } + + + + /*! + this method returns the number of the highest set bit in x + if the 'x' is zero this method returns '-1' + */ + template + sint UInt::FindLowestBitInWord(uint x) + { + if( x == 0 ) + return -1; + + uint bit = 0; + + while( (x & 1) == 0 ) + { + x = x >> 1; + ++bit; + } + + return bit; + } + + + + /*! + this method sets a special bit in the 'value' + and returns the last state of the bit (zero or one) + + bit is from <0,TTMATH_BITS_PER_UINT-1> + + e.g. + uint x = 100; + uint bit = SetBitInWord(x, 3); + now: x = 108 and bit = 0 + */ + template + uint UInt::SetBitInWord(uint & value, uint bit) + { + TTMATH_ASSERT( bit < TTMATH_BITS_PER_UINT ) + + uint mask = 1; + + if( bit > 0 ) + mask = mask << bit; + + uint last = value & mask; + value = value | mask; + + return (last != 0) ? 1 : 0; + } + + + + + + + /*! + * + * Multiplication + * + * + */ + + + /*! + multiplication: result_high:result_low = a * b + result_high - higher word of the result + result_low - lower word of the result + + this methos never returns a carry + this method is used in the second version of the multiplication algorithms + */ + template + void UInt::MulTwoWords(uint a, uint b, uint * result_high, uint * result_low) + { + #ifdef TTMATH_PLATFORM32 + + /* + on 32bit platforms we have defined 'unsigned long long int' type known as 'ulint' in ttmath namespace + this type has 64 bits, then we're using only one multiplication: 32bit * 32bit = 64bit + */ + + union uint_ + { + struct + { + uint low; // 32 bits + uint high; // 32 bits + } u_; + + ulint u; // 64 bits + } res; + + res.u = ulint(a) * ulint(b); // multiply two 32bit words, the result has 64 bits + + *result_high = res.u_.high; + *result_low = res.u_.low; + + #else + + /* + 64 bits platforms + + we don't have a native type which has 128 bits + then we're splitting 'a' and 'b' to 4 parts (high and low halves) + and using 4 multiplications (with additions and carry correctness) + */ + + uint_ a_; + uint_ b_; + uint_ res_high1, res_high2; + uint_ res_low1, res_low2; + + a_.u = a; + b_.u = b; + + /* + the multiplication is as follows (schoolbook algorithm with O(n^2) ): + + 32 bits 32 bits + + +--------------------------------+ + | a_.u_.high | a_.u_.low | + +--------------------------------+ + | b_.u_.high | b_.u_.low | + +--------------------------------+--------------------------------+ + | res_high1.u | res_low1.u | + +--------------------------------+--------------------------------+ + | res_high2.u | res_low2.u | + +--------------------------------+--------------------------------+ + + 64 bits 64 bits + */ + + + uint_ temp; + + res_low1.u = uint(b_.u_.low) * uint(a_.u_.low); + + temp.u = uint(res_low1.u_.high) + uint(b_.u_.low) * uint(a_.u_.high); + res_low1.u_.high = temp.u_.low; + res_high1.u_.low = temp.u_.high; + res_high1.u_.high = 0; + + res_low2.u_.low = 0; + temp.u = uint(b_.u_.high) * uint(a_.u_.low); + res_low2.u_.high = temp.u_.low; + + res_high2.u = uint(b_.u_.high) * uint(a_.u_.high) + uint(temp.u_.high); + + uint c = AddTwoWords(res_low1.u, res_low2.u, 0, &res_low2.u); + AddTwoWords(res_high1.u, res_high2.u, c, &res_high2.u); // there is no carry from here + + *result_high = res_high2.u; + *result_low = res_low2.u; + + #endif + } + + + + + /*! + * + * Division + * + * + */ + + + /*! + this method calculates 64bits word a:b / 32bits c (a higher, b lower word) + r = a:b / c and rest - remainder + + * + * WARNING: + * the c has to be suitably large for the result being keeped in one word, + * if c is equal zero there'll be a hardware interruption (0) + * and probably the end of your program + * + */ + template + void UInt::DivTwoWords(uint a, uint b, uint c, uint * r, uint * rest) + { + // (a < c ) for the result to be one word + TTMATH_ASSERT( c != 0 && a < c ) + + #ifdef TTMATH_PLATFORM32 + + union + { + struct + { + uint low; // 32 bits + uint high; // 32 bits + } u_; + + ulint u; // 64 bits + } ab; + + ab.u_.high = a; + ab.u_.low = b; + + *r = uint(ab.u / c); + *rest = uint(ab.u % c); + + #else + + uint_ c_; + c_.u = c; + + + if( a == 0 ) + { + *r = b / c; + *rest = b % c; + } + else + if( c_.u_.high == 0 ) + { + // higher half of 'c' is zero + // then higher half of 'a' is zero too (look at the asserts at the beginning - 'a' is smaller than 'c') + uint_ a_, b_, res_, temp1, temp2; + + a_.u = a; + b_.u = b; + + temp1.u_.high = a_.u_.low; + temp1.u_.low = b_.u_.high; + + res_.u_.high = (unsigned int)(temp1.u / c); + temp2.u_.high = (unsigned int)(temp1.u % c); + temp2.u_.low = b_.u_.low; + + res_.u_.low = (unsigned int)(temp2.u / c); + *rest = temp2.u % c; + + *r = res_.u; + } + else + { + return DivTwoWords2(a, b, c, r, rest); + } + + #endif + } + + +#ifdef TTMATH_PLATFORM64 + + + /*! + this method is available only on 64bit platforms + + the same algorithm like the third division algorithm in ttmathuint.h + but now with the radix=2^32 + */ + template + void UInt::DivTwoWords2(uint a, uint b, uint c, uint * r, uint * rest) + { + // a is not zero + // c_.u_.high is not zero + + uint_ a_, b_, c_, u_, q_; + unsigned int u3; // 32 bit + + a_.u = a; + b_.u = b; + c_.u = c; + + // normalizing + uint d = DivTwoWordsNormalize(a_, b_, c_); + + // loop from j=1 to j=0 + // the first step (for j=2) is skipped because our result is only in one word, + // (first 'q' were 0 and nothing would be changed) + u_.u_.high = a_.u_.high; + u_.u_.low = a_.u_.low; + u3 = b_.u_.high; + q_.u_.high = DivTwoWordsCalculate(u_, u3, c_); + MultiplySubtract(u_, u3, q_.u_.high, c_); + + u_.u_.high = u_.u_.low; + u_.u_.low = u3; + u3 = b_.u_.low; + q_.u_.low = DivTwoWordsCalculate(u_, u3, c_); + MultiplySubtract(u_, u3, q_.u_.low, c_); + + *r = q_.u; + + // unnormalizing for the remainder + u_.u_.high = u_.u_.low; + u_.u_.low = u3; + *rest = DivTwoWordsUnnormalize(u_.u, d); + } + + + + + template + uint UInt::DivTwoWordsNormalize(uint_ & a_, uint_ & b_, uint_ & c_) + { + uint d = 0; + + for( ; (c_.u & TTMATH_UINT_HIGHEST_BIT) == 0 ; ++d ) + { + c_.u = c_.u << 1; + + uint bc = b_.u & TTMATH_UINT_HIGHEST_BIT; // carry from 'b' + + b_.u = b_.u << 1; + a_.u = a_.u << 1; // carry bits from 'a' are simply skipped + + if( bc ) + a_.u = a_.u | 1; + } + + return d; + } + + + template + uint UInt::DivTwoWordsUnnormalize(uint u, uint d) + { + if( d == 0 ) + return u; + + u = u >> d; + + return u; + } + + + template + unsigned int UInt::DivTwoWordsCalculate(uint_ u_, unsigned int u3, uint_ v_) + { + bool next_test; + uint_ qp_, rp_, temp_; + + qp_.u = u_.u / uint(v_.u_.high); + rp_.u = u_.u % uint(v_.u_.high); + + TTMATH_ASSERT( qp_.u_.high==0 || qp_.u_.high==1 ) + + do + { + bool decrease = false; + + if( qp_.u_.high == 1 ) + decrease = true; + else + { + temp_.u_.high = rp_.u_.low; + temp_.u_.low = u3; + + if( qp_.u * uint(v_.u_.low) > temp_.u ) + decrease = true; + } + + next_test = false; + + if( decrease ) + { + --qp_.u; + rp_.u += v_.u_.high; + + if( rp_.u_.high == 0 ) + next_test = true; + } + } + while( next_test ); + + return qp_.u_.low; + } + + + template + void UInt::MultiplySubtract(uint_ & u_, unsigned int & u3, unsigned int & q, uint_ v_) + { + uint_ temp_; + + uint res_high; + uint res_low; + + MulTwoWords(v_.u, q, &res_high, &res_low); + + uint_ sub_res_high_; + uint_ sub_res_low_; + + temp_.u_.high = u_.u_.low; + temp_.u_.low = u3; + + uint c = SubTwoWords(temp_.u, res_low, 0, &sub_res_low_.u); + + temp_.u_.high = 0; + temp_.u_.low = u_.u_.high; + c = SubTwoWords(temp_.u, res_high, c, &sub_res_high_.u); + + if( c ) + { + --q; + + c = AddTwoWords(sub_res_low_.u, v_.u, 0, &sub_res_low_.u); + AddTwoWords(sub_res_high_.u, 0, c, &sub_res_high_.u); + } + + u_.u_.high = sub_res_high_.u_.low; + u_.u_.low = sub_res_low_.u_.high; + u3 = sub_res_low_.u_.low; + } + +#endif // #ifdef TTMATH_PLATFORM64 + + + +} //namespace + + +#endif //ifdef TTMATH_NOASM +#endif + + + + diff --git a/extern/ttmath/ttmathuint_x86.h b/extern/ttmath/ttmathuint_x86.h new file mode 100644 index 0000000000..1dd087f524 --- /dev/null +++ b/extern/ttmath/ttmathuint_x86.h @@ -0,0 +1,1602 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2009, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmathuint_x86 +#define headerfilettmathuint_x86 + + +#ifndef TTMATH_NOASM +#ifdef TTMATH_PLATFORM32 + + +/*! + \file ttmathuint_x86.h + \brief template class UInt with assembler code for 32bit x86 processors + + this file is included at the end of ttmathuint.h +*/ + + + +/*! + \brief a namespace for the TTMath library +*/ +namespace ttmath +{ + + /*! + returning the string represents the currect type of the library + we have following types: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + */ + template + const char * UInt::LibTypeStr() + { + #ifndef __GNUC__ + static const char info[] = "asm_vc_32"; + #endif + + #ifdef __GNUC__ + static const char info[] = "asm_gcc_32"; + #endif + + return info; + } + + + /*! + returning the currect type of the library + */ + template + LibTypeCode UInt::LibType() + { + #ifndef __GNUC__ + LibTypeCode info = asm_vc_32; + #endif + + #ifdef __GNUC__ + LibTypeCode info = asm_gcc_32; + #endif + + return info; + } + + + + /*! + * + * basic mathematic functions + * + */ + + + /*! + adding ss2 to the this and adding carry if it's defined + (this = this + ss2 + c) + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it has been) + */ + template + uint UInt::Add(const UInt & ss2, uint c) + { + uint b = value_size; + uint * p1 = table; + uint * p2 = const_cast(ss2.table); + + // we don't have to use TTMATH_REFERENCE_ASSERT here + // this algorithm doesn't require it + + #ifndef __GNUC__ + + // this part might be compiled with for example visual c + + __asm + { + push eax + push ebx + push ecx + push edx + push esi + + mov ecx,[b] + + mov ebx,[p1] + mov esi,[p2] + + xor edx,edx // edx=0 + mov eax,[c] + neg eax // CF=1 if rax!=0 , CF=0 if rax==0 + + ttmath_loop: + mov eax,[esi+edx*4] + adc [ebx+edx*4],eax + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx + mov [c], ecx + + pop esi + pop edx + pop ecx + pop ebx + pop eax + } + + + + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + // this part should be compiled with gcc + + __asm__ __volatile__( + + "xorl %%edx, %%edx \n" + "negl %%eax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "movl (%%esi,%%edx,4), %%eax \n" + "adcl %%eax, (%%ebx,%%edx,4) \n" + + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "adc %%ecx, %%ecx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1), "S" (p2) + : "cc", "memory" ); + #endif + + TTMATH_LOGC("UInt::Add", c) + + return c; + } + + + + /*! + adding one word (at a specific position) + and returning a carry (if it has been) + + e.g. + + if we've got (value_size=3): + table[0] = 10; + table[1] = 30; + table[2] = 5; + and we call: + AddInt(2,1) + then it'll be: + table[0] = 10; + table[1] = 30 + 2; + table[2] = 5; + + of course if there was a carry from table[2] it would be returned + */ + template + uint UInt::AddInt(uint value, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size ) + + #ifndef __GNUC__ + + __asm + { + push eax + push ebx + push ecx + push edx + + mov ecx, [b] + sub ecx, [index] + + mov edx, [index] + mov ebx, [p1] + + mov eax, [value] + + ttmath_loop: + add [ebx+edx*4], eax + jnc ttmath_end + + mov eax, 1 + inc edx + dec ecx + jnz ttmath_loop + + ttmath_end: + setc al + movzx edx, al + mov [c], edx + + pop edx + pop ecx + pop ebx + pop eax + } + + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subl %%edx, %%ecx \n" + + "1: \n" + "addl %%eax, (%%ebx,%%edx,4) \n" + "jnc 2f \n" + + "movl $1, %%eax \n" + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%edx \n" + + : "=d" (c), "=a" (dummy), "=c" (dummy2) + : "0" (index), "1" (value), "2" (b), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::AddInt", c) + + return c; + } + + + + + /*! + adding only two unsigned words to the existing value + and these words begin on the 'index' position + (it's used in the multiplication algorithm 2) + + index should be equal or smaller than value_size-2 (index <= value_size-2) + x1 - lower word, x2 - higher word + + for example if we've got value_size equal 4 and: + table[0] = 3 + table[1] = 4 + table[2] = 5 + table[3] = 6 + then let + x1 = 10 + x2 = 20 + and + index = 1 + + the result of this method will be: + table[0] = 3 + table[1] = 4 + x1 = 14 + table[2] = 5 + x2 = 25 + table[3] = 6 + + and no carry at the end of table[3] + + (of course if there was a carry in table[2](5+20) then + this carry would be passed to the table[3] etc.) + */ + template + uint UInt::AddTwoInts(uint x2, uint x1, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size - 1 ) + + #ifndef __GNUC__ + __asm + { + push eax + push ebx + push ecx + push edx + + mov ecx, [b] + sub ecx, [index] + + mov ebx, [p1] + mov edx, [index] + + mov eax, [x1] + add [ebx+edx*4], eax + inc edx + dec ecx + + mov eax, [x2] + + ttmath_loop: + adc [ebx+edx*4], eax + jnc ttmath_end + + mov eax, 0 + inc edx + dec ecx + jnz ttmath_loop + + ttmath_end: + setc al + movzx edx, al + mov [c], edx + + pop edx + pop ecx + pop ebx + pop eax + + } + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subl %%edx, %%ecx \n" + + "addl %%esi, (%%ebx,%%edx,4) \n" + "incl %%edx \n" + "decl %%ecx \n" + + "1: \n" + "adcl %%eax, (%%ebx,%%edx,4) \n" + "jnc 2f \n" + + "mov $0, %%eax \n" + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%eax \n" + + : "=a" (c), "=c" (dummy), "=d" (dummy2) + : "0" (x2), "1" (b), "2" (index), "b" (p1), "S" (x1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::AddTwoInts", c) + + return c; + } + + + + /*! + this static method addes one vector to the other + 'ss1' is larger in size or equal to 'ss2' + + ss1 points to the first (larger) vector + ss2 points to the second vector + ss1_size - size of the ss1 (and size of the result too) + ss2_size - size of the ss2 + result - is the result vector (which has size the same as ss1: ss1_size) + + Example: ss1_size is 5, ss2_size is 3 + ss1: ss2: result (output): + 5 1 5+1 + 4 3 4+3 + 2 7 2+7 + 6 6 + 9 9 + of course the carry is propagated and will be returned from the last item + (this method is used by the Karatsuba multiplication algorithm) + */ + template + uint UInt::AddVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + TTMATH_ASSERT( ss1_size >= ss2_size ) + + uint rest = ss1_size - ss2_size; + uint c; + + #ifndef __GNUC__ + + // this part might be compiled with for example visual c + __asm + { + pushad + + mov ecx, [ss2_size] + xor edx, edx // edx = 0, cf = 0 + + mov esi, [ss1] + mov ebx, [ss2] + mov edi, [result] + + ttmath_loop: + mov eax, [esi+edx*4] + adc eax, [ebx+edx*4] + mov [edi+edx*4], eax + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx // ecx has the cf state + + mov ebx, [rest] + or ebx, ebx + jz ttmath_end + + xor ebx, ebx // ebx = 0 + neg ecx // setting cf from ecx + mov ecx, [rest] // ecx is != 0 + + ttmath_loop2: + mov eax, [esi+edx*4] + adc eax, ebx + mov [edi+edx*4], eax + + inc edx + dec ecx + jnz ttmath_loop2 + + adc ecx, ecx + + ttmath_end: + mov [c], ecx + + popad + } + + #endif + + + #ifdef __GNUC__ + + // this part should be compiled with gcc + uint dummy1, dummy2, dummy3; + + __asm__ __volatile__( + "push %%edx \n" + "xor %%edx, %%edx \n" // edx = 0, cf = 0 + "1: \n" + "mov (%%esi,%%edx,4), %%eax \n" + "adc (%%ebx,%%edx,4), %%eax \n" + "mov %%eax, (%%edi,%%edx,4) \n" + + "inc %%edx \n" + "dec %%ecx \n" + "jnz 1b \n" + + "adc %%ecx, %%ecx \n" // ecx has the cf state + "pop %%eax \n" // eax = rest + + "or %%eax, %%eax \n" + "jz 3f \n" + + "xor %%ebx, %%ebx \n" // ebx = 0 + "neg %%ecx \n" // setting cf from ecx + "mov %%eax, %%ecx \n" // ecx=rest and is != 0 + "2: \n" + "mov (%%esi, %%edx, 4), %%eax \n" + "adc %%ebx, %%eax \n" + "mov %%eax, (%%edi, %%edx, 4) \n" + + "inc %%edx \n" + "dec %%ecx \n" + "jnz 2b \n" + + "adc %%ecx, %%ecx \n" + "3: \n" + + : "=a" (dummy1), "=b" (dummy2), "=c" (c), "=d" (dummy3) + : "1" (ss2), "2" (ss2_size), "3" (rest), "S" (ss1), "D" (result) + : "cc", "memory" ); + + #endif + + TTMATH_VECTOR_LOGC("UInt::AddVector", c, result, ss1_size) + + return c; + } + + + /*! + subtracting ss2 from the 'this' and subtracting + carry if it has been defined + (this = this - ss2 - c) + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it has been) + */ + template + uint UInt::Sub(const UInt & ss2, uint c) + { + uint b = value_size; + uint * p1 = table; + uint * p2 = const_cast(ss2.table); + + // we don't have to use TTMATH_REFERENCE_ASSERT here + // this algorithm doesn't require it + + #ifndef __GNUC__ + + __asm + { + push eax + push ebx + push ecx + push edx + push esi + + mov ecx,[b] + + mov ebx,[p1] + mov esi,[p2] + + xor edx,edx // edx=0 + mov eax,[c] + neg eax // CF=1 if rax!=0 , CF=0 if rax==0 + + ttmath_loop: + mov eax,[esi+edx*4] + sbb [ebx+edx*4],eax + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx + mov [c], ecx + + pop esi + pop edx + pop ecx + pop ebx + pop eax + } + + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "xorl %%edx, %%edx \n" + "negl %%eax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "movl (%%esi,%%edx,4), %%eax \n" + "sbbl %%eax, (%%ebx,%%edx,4) \n" + + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "adc %%ecx, %%ecx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1), "S" (p2) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Sub", c) + + return c; + } + + + + + /*! + this method subtracts one word (at a specific position) + and returns a carry (if it was) + + e.g. + + if we've got (value_size=3): + table[0] = 10; + table[1] = 30; + table[2] = 5; + and we call: + SubInt(2,1) + then it'll be: + table[0] = 10; + table[1] = 30 - 2; + table[2] = 5; + + of course if there was a carry from table[2] it would be returned + */ + template + uint UInt::SubInt(uint value, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size ) + + #ifndef __GNUC__ + + __asm + { + push eax + push ebx + push ecx + push edx + + mov ecx, [b] + sub ecx, [index] + + mov edx, [index] + mov ebx, [p1] + + mov eax, [value] + + ttmath_loop: + sub [ebx+edx*4], eax + jnc ttmath_end + + mov eax, 1 + inc edx + dec ecx + jnz ttmath_loop + + ttmath_end: + setc al + movzx edx, al + mov [c], edx + + pop edx + pop ecx + pop ebx + pop eax + } + + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subl %%edx, %%ecx \n" + + "1: \n" + "subl %%eax, (%%ebx,%%edx,4) \n" + "jnc 2f \n" + + "movl $1, %%eax \n" + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%edx \n" + + : "=d" (c), "=a" (dummy), "=c" (dummy2) + : "0" (index), "1" (value), "2" (b), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::SubInt", c) + + return c; + } + + + + /*! + this static method subtractes one vector from the other + 'ss1' is larger in size or equal to 'ss2' + + ss1 points to the first (larger) vector + ss2 points to the second vector + ss1_size - size of the ss1 (and size of the result too) + ss2_size - size of the ss2 + result - is the result vector (which has size the same as ss1: ss1_size) + + Example: ss1_size is 5, ss2_size is 3 + ss1: ss2: result (output): + 5 1 5-1 + 4 3 4-3 + 2 7 2-7 + 6 6-1 (the borrow from previous item) + 9 9 + return (carry): 0 + of course the carry (borrow) is propagated and will be returned from the last item + (this method is used by the Karatsuba multiplication algorithm) + */ + template + uint UInt::SubVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + TTMATH_ASSERT( ss1_size >= ss2_size ) + + uint rest = ss1_size - ss2_size; + uint c; + + #ifndef __GNUC__ + + // this part might be compiled with for example visual c + + /* + the asm code is nearly the same as in AddVector + only two instructions 'adc' are changed to 'sbb' + */ + __asm + { + pushad + + mov ecx, [ss2_size] + xor edx, edx // edx = 0, cf = 0 + + mov esi, [ss1] + mov ebx, [ss2] + mov edi, [result] + + ttmath_loop: + mov eax, [esi+edx*4] + sbb eax, [ebx+edx*4] + mov [edi+edx*4], eax + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx // ecx has the cf state + + mov ebx, [rest] + or ebx, ebx + jz ttmath_end + + xor ebx, ebx // ebx = 0 + neg ecx // setting cf from ecx + mov ecx, [rest] // ecx is != 0 + + ttmath_loop2: + mov eax, [esi+edx*4] + sbb eax, ebx + mov [edi+edx*4], eax + + inc edx + dec ecx + jnz ttmath_loop2 + + adc ecx, ecx + + ttmath_end: + mov [c], ecx + + popad + } + + #endif + + + #ifdef __GNUC__ + + // this part should be compiled with gcc + uint dummy1, dummy2, dummy3; + + __asm__ __volatile__( + "push %%edx \n" + "xor %%edx, %%edx \n" // edx = 0, cf = 0 + "1: \n" + "mov (%%esi,%%edx,4), %%eax \n" + "sbb (%%ebx,%%edx,4), %%eax \n" + "mov %%eax, (%%edi,%%edx,4) \n" + + "inc %%edx \n" + "dec %%ecx \n" + "jnz 1b \n" + + "adc %%ecx, %%ecx \n" // ecx has the cf state + "pop %%eax \n" // eax = rest + + "or %%eax, %%eax \n" + "jz 3f \n" + + "xor %%ebx, %%ebx \n" // ebx = 0 + "neg %%ecx \n" // setting cf from ecx + "mov %%eax, %%ecx \n" // ecx=rest and is != 0 + "2: \n" + "mov (%%esi, %%edx, 4), %%eax \n" + "sbb %%ebx, %%eax \n" + "mov %%eax, (%%edi, %%edx, 4) \n" + + "inc %%edx \n" + "dec %%ecx \n" + "jnz 2b \n" + + "adc %%ecx, %%ecx \n" + "3: \n" + + : "=a" (dummy1), "=b" (dummy2), "=c" (c), "=d" (dummy3) + : "1" (ss2), "2" (ss2_size), "3" (rest), "S" (ss1), "D" (result) + : "cc", "memory" ); + + #endif + + TTMATH_VECTOR_LOGC("UInt::SubVector", c, result, ss1_size) + + return c; + } + + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bit* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2_one(1) there'll be 010100001 and Rcl2_one returns 0 + */ + template + uint UInt::Rcl2_one(uint c) + { + uint b = value_size; + uint * p1 = table; + + #ifndef __GNUC__ + __asm + { + push ebx + push ecx + push edx + + mov ebx, [p1] + xor edx, edx + mov ecx, [c] + neg ecx + mov ecx, [b] + + ttmath_loop: + rcl dword ptr [ebx+edx*4], 1 + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx + mov [c], ecx + + pop edx + pop ecx + pop ebx + } + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "xorl %%edx, %%edx \n" // edx=0 + "negl %%eax \n" // CF=1 if eax!=0 , CF=0 if eax==0 + + "1: \n" + "rcll $1, (%%ebx, %%edx, 4) \n" + + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "adcl %%ecx, %%ecx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcl2_one", c) + + return c; + } + + + + /*! + this method moves all bits into the right hand side + c -> this -> return value + + the highest *bit* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2_one(1) there'll be 100000001 and Rcr2_one returns 0 + */ + template + uint UInt::Rcr2_one(uint c) + { + uint b = value_size; + uint * p1 = table; + + #ifndef __GNUC__ + __asm + { + push ebx + push ecx + + mov ebx, [p1] + mov ecx, [c] + neg ecx + mov ecx, [b] + + ttmath_loop: + rcr dword ptr [ebx+ecx*4-4], 1 + + dec ecx + jnz ttmath_loop + + adc ecx, ecx + mov [c], ecx + + pop ecx + pop ebx + } + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ __volatile__( + + "negl %%eax \n" // CF=1 if eax!=0 , CF=0 if eax==0 + + "1: \n" + "rcrl $1, -4(%%ebx, %%ecx, 4) \n" + + "decl %%ecx \n" + "jnz 1b \n" + + "adcl %%ecx, %%ecx \n" + + : "=c" (c), "=a" (dummy) + : "0" (b), "1" (c), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcr2_one", c) + + return c; + } + + + +#ifdef _MSC_VER +#pragma warning (disable : 4731) +//warning C4731: frame pointer register 'ebp' modified by inline assembly code +#endif + + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bits* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2(3, 1) there'll be 010000111 and Rcl2 returns 1 + */ + template + uint UInt::Rcl2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits edx -> cf) (cl times) + "movl %%edx, %%ebp \n" // ebp = edx = mask + "movl %%esi, %%ecx \n" + + "xorl %%edx, %%edx \n" + "movl %%edx, %%esi \n" + "orl %%eax, %%eax \n" + "cmovnz %%ebp, %%esi \n" // if(c) esi=mask else esi=0 + + "1: \n" + "roll %%cl, (%%ebx,%%edx,4) \n" + + "movl (%%ebx,%%edx,4), %%eax \n" + "andl %%ebp, %%eax \n" + "xorl %%eax, (%%ebx,%%edx,4) \n" + "orl %%esi, (%%ebx,%%edx,4) \n" + "movl %%eax, %%esi \n" + + "incl %%edx \n" + "decl %%edi \n" + "jnz 1b \n" + + "and $1, %%eax \n" + + "pop %%ebp \n" + + : "=a" (c), "=D" (dummy), "=S" (dummy2), "=d" (dummy3) + : "0" (c), "1" (b), "b" (p1), "c" (bits) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcl2", c) + + return c; + } + + + + + /*! + this method moves all bits into the right hand side + C -> this -> return value + + the highest *bits* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2(2, 1) there'll be 110000000 and Rcr2 returns 1 + */ + template + uint UInt::Rcr2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits + sint UInt::FindLeadingBitInWord(uint x) + { + sint result; + + #ifndef __GNUC__ + __asm + { + push eax + push edx + + mov edx,-1 + bsr eax,[x] + cmovz eax,edx + mov [result], eax + + pop edx + pop eax + } + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ ( + + "movl $-1, %1 \n" + "bsrl %2, %0 \n" + "cmovz %1, %0 \n" + + : "=r" (result), "=&r" (dummy) + : "r" (x) + : "cc" ); + + #endif + + return result; + } + + + + /* + this method returns the number of the smallest set bit in one 32-bit word + if the 'x' is zero this method returns '-1' + */ + template + sint UInt::FindLowestBitInWord(uint x) + { + sint result; + + #ifndef __GNUC__ + __asm + { + push eax + push edx + + mov edx,-1 + bsf eax,[x] + cmovz eax,edx + mov [result], eax + + pop edx + pop eax + } + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ ( + + "movl $-1, %1 \n" + "bsfl %2, %0 \n" + "cmovz %1, %0 \n" + + : "=r" (result), "=&r" (dummy) + : "r" (x) + : "cc" ); + + #endif + + return result; + } + + + + /*! + this method sets a special bit in the 'value' + and returns the last state of the bit (zero or one) + + bit is from <0,31> + e.g. + uint x = 100; + uint bit = SetBitInWord(x, 3); + now: x = 108 and bit = 0 + */ + template + uint UInt::SetBitInWord(uint & value, uint bit) + { + TTMATH_ASSERT( bit < TTMATH_BITS_PER_UINT ) + + uint old_bit; + uint v = value; + + #ifndef __GNUC__ + __asm + { + push ebx + push eax + + mov eax, [v] + mov ebx, [bit] + bts eax, ebx + mov [v], eax + + setc bl + movzx ebx, bl + mov [old_bit], ebx + + pop eax + pop ebx + } + #endif + + + #ifdef __GNUC__ + __asm__ ( + + "btsl %%ebx, %%eax \n" + "setc %%bl \n" + "movzx %%bl, %%ebx \n" + + : "=a" (v), "=b" (old_bit) + : "0" (v), "1" (bit) + : "cc" ); + + #endif + + value = v; + + return old_bit; + } + + + + + /*! + multiplication: result_high:result_low = a * b + result_high - higher word of the result + result_low - lower word of the result + + this methos never returns a carry + this method is used in the second version of the multiplication algorithms + */ + template + void UInt::MulTwoWords(uint a, uint b, uint * result_high, uint * result_low) + { + /* + we must use these temporary variables in order to inform the compilator + that value pointed with result1 and result2 has changed + + this has no effect in visual studio but it's useful when + using gcc and options like -Ox + */ + uint result1_; + uint result2_; + + #ifndef __GNUC__ + + __asm + { + push eax + push edx + + mov eax, [a] + mul dword ptr [b] + + mov [result2_], edx + mov [result1_], eax + + pop edx + pop eax + } + + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "mull %%edx \n" + + : "=a" (result1_), "=d" (result2_) + : "0" (a), "1" (b) + : "cc" ); + + #endif + + + *result_low = result1_; + *result_high = result2_; + } + + + + + + /*! + * + * Division + * + * + */ + + + + + /*! + this method calculates 64bits word a:b / 32bits c (a higher, b lower word) + r = a:b / c and rest - remainder + + * + * WARNING: + * if r (one word) is too small for the result or c is equal zero + * there'll be a hardware interruption (0) + * and probably the end of your program + * + */ + template + void UInt::DivTwoWords(uint a, uint b, uint c, uint * r, uint * rest) + { + uint r_; + uint rest_; + /* + these variables have similar meaning like those in + the multiplication algorithm MulTwoWords + */ + + TTMATH_ASSERT( c != 0 ) + + #ifndef __GNUC__ + __asm + { + push eax + push edx + + mov edx, [a] + mov eax, [b] + div dword ptr [c] + + mov [r_], eax + mov [rest_], edx + + pop edx + pop eax + } + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "divl %%ecx \n" + + : "=a" (r_), "=d" (rest_) + : "0" (b), "1" (a), "c" (c) + : "cc" ); + + #endif + + + *r = r_; + *rest = rest_; + + } + + + +} //namespace + + + +#endif //ifdef TTMATH_PLATFORM32 +#endif //ifndef TTMATH_NOASM +#endif diff --git a/extern/ttmath/ttmathuint_x86_64.h b/extern/ttmath/ttmathuint_x86_64.h new file mode 100644 index 0000000000..188fc5e7bd --- /dev/null +++ b/extern/ttmath/ttmathuint_x86_64.h @@ -0,0 +1,1146 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2010, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef headerfilettmathuint_x86_64 +#define headerfilettmathuint_x86_64 + + +#ifndef TTMATH_NOASM +#ifdef TTMATH_PLATFORM64 + + +/*! + \file ttmathuint_x86_64.h + \brief template class UInt with assembler code for 64bit x86_64 processors + + this file is included at the end of ttmathuint.h +*/ + +#ifndef __GNUC__ +#include +#endif + + +namespace ttmath +{ + + #ifndef __GNUC__ + + extern "C" + { + uint __fastcall ttmath_adc_x64(uint* p1, const uint* p2, uint nSize, uint c); + uint __fastcall ttmath_addindexed_x64(uint* p1, uint nSize, uint nPos, uint nValue); + uint __fastcall ttmath_addindexed2_x64(uint* p1, uint nSize, uint nPos, uint nValue1, uint nValue2); + uint __fastcall ttmath_addvector_x64(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result); + uint __fastcall ttmath_sbb_x64(uint* p1, const uint* p2, uint nSize, uint c); + uint __fastcall ttmath_subindexed_x64(uint* p1, uint nSize, uint nPos, uint nValue); + uint __fastcall ttmath_subvector_x64(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result); + uint __fastcall ttmath_rcl_x64(uint* p1, uint nSize, uint nLowestBit); + uint __fastcall ttmath_rcr_x64(uint* p1, uint nSize, uint nLowestBit); + uint __fastcall ttmath_div_x64(uint* pnValHi, uint* pnValLo, uint nDiv); + uint __fastcall ttmath_rcl2_x64(uint* p1, uint nSize, uint nBits, uint c); + uint __fastcall ttmath_rcr2_x64(uint* p1, uint nSize, uint nBits, uint c); + }; + #endif + + + /*! + returning the string represents the currect type of the library + we have following types: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + */ + template + const char * UInt::LibTypeStr() + { + #ifndef __GNUC__ + static const char info[] = "asm_vc_64"; + #endif + + #ifdef __GNUC__ + static const char info[] = "asm_gcc_64"; + #endif + + return info; + } + + + /*! + returning the currect type of the library + */ + template + LibTypeCode UInt::LibType() + { + #ifndef __GNUC__ + LibTypeCode info = asm_vc_64; + #endif + + #ifdef __GNUC__ + LibTypeCode info = asm_gcc_64; + #endif + + return info; + } + + + /*! + * + * basic mathematic functions + * + */ + + + + /*! + this method adding ss2 to the this and adding carry if it's defined + (this = this + ss2 + c) + + ***this method is created only on a 64bit platform*** + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it was) + */ + template + uint UInt::Add(const UInt & ss2, uint c) + { + uint b = value_size; + uint * p1 = table; + const uint * p2 = ss2.table; + + // we don't have to use TTMATH_REFERENCE_ASSERT here + // this algorithm doesn't require it + + #ifndef __GNUC__ + c = ttmath_adc_x64(p1,p2,b,c); + #endif + + #ifdef __GNUC__ + uint dummy, dummy2; + + /* + this part should be compiled with gcc + */ + __asm__ __volatile__( + + "xorq %%rdx, %%rdx \n" + "negq %%rax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "movq (%%rsi,%%rdx,8), %%rax \n" + "adcq %%rax, (%%rbx,%%rdx,8) \n" + + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "adcq %%rcx, %%rcx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1), "S" (p2) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Add", c) + + return c; + } + + + + /*! + this method adds one word (at a specific position) + and returns a carry (if it was) + + ***this method is created only on a 64bit platform*** + + + if we've got (value_size=3): + table[0] = 10; + table[1] = 30; + table[2] = 5; + and we call: + AddInt(2,1) + then it'll be: + table[0] = 10; + table[1] = 30 + 2; + table[2] = 5; + + of course if there was a carry from table[2] it would be returned + */ + template + uint UInt::AddInt(uint value, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size ) + + #ifndef __GNUC__ + c = ttmath_addindexed_x64(p1,b,index,value); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subq %%rdx, %%rcx \n" + + "1: \n" + "addq %%rax, (%%rbx,%%rdx,8) \n" + "jnc 2f \n" + + "movq $1, %%rax \n" + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%rdx \n" + + : "=d" (c), "=a" (dummy), "=c" (dummy2) + : "0" (index), "1" (value), "2" (b), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::AddInt", c) + + return c; + } + + + + /*! + this method adds only two unsigned words to the existing value + and these words begin on the 'index' position + (it's used in the multiplication algorithm 2) + + ***this method is created only on a 64bit platform*** + + index should be equal or smaller than value_size-2 (index <= value_size-2) + x1 - lower word, x2 - higher word + + for example if we've got value_size equal 4 and: + table[0] = 3 + table[1] = 4 + table[2] = 5 + table[3] = 6 + then let + x1 = 10 + x2 = 20 + and + index = 1 + + the result of this method will be: + table[0] = 3 + table[1] = 4 + x1 = 14 + table[2] = 5 + x2 = 25 + table[3] = 6 + + and no carry at the end of table[3] + + (of course if there was a carry in table[2](5+20) then + this carry would be passed to the table[3] etc.) + */ + template + uint UInt::AddTwoInts(uint x2, uint x1, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size - 1 ) + + #ifndef __GNUC__ + c = ttmath_addindexed2_x64(p1,b,index,x1,x2); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subq %%rdx, %%rcx \n" + + "addq %%rsi, (%%rbx,%%rdx,8) \n" + "incq %%rdx \n" + "decq %%rcx \n" + + "1: \n" + "adcq %%rax, (%%rbx,%%rdx,8) \n" + "jnc 2f \n" + + "mov $0, %%rax \n" + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%rax \n" + + : "=a" (c), "=c" (dummy), "=d" (dummy2) + : "0" (x2), "1" (b), "2" (index), "b" (p1), "S" (x1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::AddTwoInts", c) + + return c; + } + + + + /*! + this static method addes one vector to the other + 'ss1' is larger in size or equal to 'ss2' + + ss1 points to the first (larger) vector + ss2 points to the second vector + ss1_size - size of the ss1 (and size of the result too) + ss2_size - size of the ss2 + result - is the result vector (which has size the same as ss1: ss1_size) + + Example: ss1_size is 5, ss2_size is 3 + ss1: ss2: result (output): + 5 1 5+1 + 4 3 4+3 + 2 7 2+7 + 6 6 + 9 9 + of course the carry is propagated and will be returned from the last item + (this method is used by the Karatsuba multiplication algorithm) + */ + template + uint UInt::AddVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + TTMATH_ASSERT( ss1_size >= ss2_size ) + + uint c; + + #ifndef __GNUC__ + c = ttmath_addvector_x64(ss1, ss2, ss1_size, ss2_size, result); + #endif + + + #ifdef __GNUC__ + uint dummy1, dummy2, dummy3; + uint rest = ss1_size - ss2_size; + + // this part should be compiled with gcc + + __asm__ __volatile__( + "mov %%rdx, %%r8 \n" + "xor %%rdx, %%rdx \n" // rdx = 0, cf = 0 + "1: \n" + "mov (%%rsi,%%rdx,8), %%rax \n" + "adc (%%rbx,%%rdx,8), %%rax \n" + "mov %%rax, (%%rdi,%%rdx,8) \n" + + "inc %%rdx \n" + "dec %%rcx \n" + "jnz 1b \n" + + "adc %%rcx, %%rcx \n" // rcx has the cf state + + "or %%r8, %%r8 \n" + "jz 3f \n" + + "xor %%rbx, %%rbx \n" // ebx = 0 + "neg %%rcx \n" // setting cf from rcx + "mov %%r8, %%rcx \n" // rcx=rest and is != 0 + "2: \n" + "mov (%%rsi, %%rdx, 8), %%rax \n" + "adc %%rbx, %%rax \n" + "mov %%rax, (%%rdi, %%rdx, 8) \n" + + "inc %%rdx \n" + "dec %%rcx \n" + "jnz 2b \n" + + "adc %%rcx, %%rcx \n" + "3: \n" + + : "=a" (dummy1), "=b" (dummy2), "=c" (c), "=d" (dummy3) + : "1" (ss2), "2" (ss2_size), "3" (rest), "S" (ss1), "D" (result) + : "%r8", "cc", "memory" ); + + #endif + + TTMATH_VECTOR_LOGC("UInt::AddVector", c, result, ss1_size) + + return c; + } + + + + /*! + this method's subtracting ss2 from the 'this' and subtracting + carry if it has been defined + (this = this - ss2 - c) + + ***this method is created only on a 64bit platform*** + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it was) + */ + template + uint UInt::Sub(const UInt & ss2, uint c) + { + uint b = value_size; + uint * p1 = table; + const uint * p2 = ss2.table; + + // we don't have to use TTMATH_REFERENCE_ASSERT here + // this algorithm doesn't require it + + #ifndef __GNUC__ + c = ttmath_sbb_x64(p1,p2,b,c); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "xorq %%rdx, %%rdx \n" + "negq %%rax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "movq (%%rsi,%%rdx,8), %%rax \n" + "sbbq %%rax, (%%rbx,%%rdx,8) \n" + + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "adcq %%rcx, %%rcx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1), "S" (p2) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Sub", c) + + return c; + } + + + + /*! + this method subtracts one word (at a specific position) + and returns a carry (if it was) + + ***this method is created only on a 64bit platform*** + + if we've got (value_size=3): + table[0] = 10; + table[1] = 30; + table[2] = 5; + and we call: + SubInt(2,1) + then it'll be: + table[0] = 10; + table[1] = 30 - 2; + table[2] = 5; + + of course if there was a carry from table[2] it would be returned + */ + template + uint UInt::SubInt(uint value, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size ) + + #ifndef __GNUC__ + c = ttmath_subindexed_x64(p1,b,index,value); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subq %%rdx, %%rcx \n" + + "1: \n" + "subq %%rax, (%%rbx,%%rdx,8) \n" + "jnc 2f \n" + + "movq $1, %%rax \n" + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%rdx \n" + + : "=d" (c), "=a" (dummy), "=c" (dummy2) + : "0" (index), "1" (value), "2" (b), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::SubInt", c) + + return c; + } + + + /*! + this static method subtractes one vector from the other + 'ss1' is larger in size or equal to 'ss2' + + ss1 points to the first (larger) vector + ss2 points to the second vector + ss1_size - size of the ss1 (and size of the result too) + ss2_size - size of the ss2 + result - is the result vector (which has size the same as ss1: ss1_size) + + Example: ss1_size is 5, ss2_size is 3 + ss1: ss2: result (output): + 5 1 5-1 + 4 3 4-3 + 2 7 2-7 + 6 6-1 (the borrow from previous item) + 9 9 + return (carry): 0 + of course the carry (borrow) is propagated and will be returned from the last item + (this method is used by the Karatsuba multiplication algorithm) + */ + template + uint UInt::SubVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + TTMATH_ASSERT( ss1_size >= ss2_size ) + + uint c; + + #ifndef __GNUC__ + c = ttmath_subvector_x64(ss1, ss2, ss1_size, ss2_size, result); + #endif + + + #ifdef __GNUC__ + + // the asm code is nearly the same as in AddVector + // only two instructions 'adc' are changed to 'sbb' + + uint dummy1, dummy2, dummy3; + uint rest = ss1_size - ss2_size; + + __asm__ __volatile__( + "mov %%rdx, %%r8 \n" + "xor %%rdx, %%rdx \n" // rdx = 0, cf = 0 + "1: \n" + "mov (%%rsi,%%rdx,8), %%rax \n" + "sbb (%%rbx,%%rdx,8), %%rax \n" + "mov %%rax, (%%rdi,%%rdx,8) \n" + + "inc %%rdx \n" + "dec %%rcx \n" + "jnz 1b \n" + + "adc %%rcx, %%rcx \n" // rcx has the cf state + + "or %%r8, %%r8 \n" + "jz 3f \n" + + "xor %%rbx, %%rbx \n" // ebx = 0 + "neg %%rcx \n" // setting cf from rcx + "mov %%r8, %%rcx \n" // rcx=rest and is != 0 + "2: \n" + "mov (%%rsi, %%rdx, 8), %%rax \n" + "sbb %%rbx, %%rax \n" + "mov %%rax, (%%rdi, %%rdx, 8) \n" + + "inc %%rdx \n" + "dec %%rcx \n" + "jnz 2b \n" + + "adc %%rcx, %%rcx \n" + "3: \n" + + : "=a" (dummy1), "=b" (dummy2), "=c" (c), "=d" (dummy3) + : "1" (ss2), "2" (ss2_size), "3" (rest), "S" (ss1), "D" (result) + : "%r8", "cc", "memory" ); + + #endif + + TTMATH_VECTOR_LOGC("UInt::SubVector", c, result, ss1_size) + + return c; + } + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bit* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2_one(1) there'll be 010100001 and Rcl2_one returns 0 + + ***this method is created only on a 64bit platform*** + */ + template + uint UInt::Rcl2_one(uint c) + { + sint b = value_size; + uint * p1 = table; + + + #ifndef __GNUC__ + c = ttmath_rcl_x64(p1,b,c); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "xorq %%rdx, %%rdx \n" // rdx=0 + "negq %%rax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "rclq $1, (%%rbx, %%rdx, 8) \n" + + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "adcq %%rcx, %%rcx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcl2_one", c) + + return c; + } + + + /*! + this method moves all bits into the right hand side + c -> this -> return value + + the highest *bit* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2_one(1) there'll be 100000001 and Rcr2_one returns 0 + + ***this method is created only on a 64bit platform*** + */ + template + uint UInt::Rcr2_one(uint c) + { + sint b = value_size; + uint * p1 = table; + + + #ifndef __GNUC__ + c = ttmath_rcr_x64(p1,b,c); + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ __volatile__( + + "negq %%rax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "rcrq $1, -8(%%rbx, %%rcx, 8) \n" + + "decq %%rcx \n" + "jnz 1b \n" + + "adcq %%rcx, %%rcx \n" + + : "=c" (c), "=a" (dummy) + : "0" (b), "1" (c), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcr2_one", c) + + return c; + } + + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bits* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2(3, 1) there'll be 010000111 and Rcl2 returns 1 + + ***this method is created only on a 64bit platform*** + */ + template + uint UInt::Rcl2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits this -> return value + + the highest *bits* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2(2, 1) there'll be 110000000 and Rcr2 returns 1 + + ***this method is created only on a 64bit platform*** + */ + template + uint UInt::Rcr2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits + sint UInt::FindLeadingBitInWord(uint x) + { + sint result; + + + #ifndef __GNUC__ + + unsigned long nIndex = 0; + + if( _BitScanReverse64(&nIndex,x) == 0 ) + result = -1; + else + result = nIndex; + + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ ( + + "movq $-1, %1 \n" + "bsrq %2, %0 \n" + "cmovz %1, %0 \n" + + : "=r" (result), "=&r" (dummy) + : "r" (x) + : "cc" ); + + #endif + + + return result; + } + + + /* + this method returns the number of the highest set bit in one 64-bit word + if the 'x' is zero this method returns '-1' + + ***this method is created only on a 64bit platform*** + */ + template + sint UInt::FindLowestBitInWord(uint x) + { + sint result; + + + #ifndef __GNUC__ + + unsigned long nIndex = 0; + + if( _BitScanForward64(&nIndex,x) == 0 ) + result = -1; + else + result = nIndex; + + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ ( + + "movq $-1, %1 \n" + "bsfq %2, %0 \n" + "cmovz %1, %0 \n" + + : "=r" (result), "=&r" (dummy) + : "r" (x) + : "cc" ); + + #endif + + + return result; + } + + + /*! + this method sets a special bit in the 'value' + and returns the last state of the bit (zero or one) + + ***this method is created only on a 64bit platform*** + + bit is from <0,63> + + e.g. + uint x = 100; + uint bit = SetBitInWord(x, 3); + now: x = 108 and bit = 0 + */ + template + uint UInt::SetBitInWord(uint & value, uint bit) + { + TTMATH_ASSERT( bit < TTMATH_BITS_PER_UINT ) + + uint old_bit; + uint v = value; + + + #ifndef __GNUC__ + old_bit = _bittestandset64((__int64*)&value,bit) != 0; + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "btsq %%rbx, %%rax \n" + "setc %%bl \n" + "movzx %%bl, %%rbx \n" + + : "=a" (v), "=b" (old_bit) + : "0" (v), "1" (bit) + : "cc" ); + + #endif + + value = v; + + return old_bit; + } + + + /*! + * + * Multiplication + * + * + */ + + + /*! + multiplication: result_high:result_low = a * b + result_high - higher word of the result + result_low - lower word of the result + + this methos never returns a carry + this method is used in the second version of the multiplication algorithms + + ***this method is created only on a 64bit platform*** + */ + template + void UInt::MulTwoWords(uint a, uint b, uint * result_high, uint * result_low) + { + /* + we must use these temporary variables in order to inform the compilator + that value pointed with result1 and result2 has changed + + this has no effect in visual studio but it's usefull when + using gcc and options like -O + */ + uint result1_; + uint result2_; + + + #ifndef __GNUC__ + result1_ = _umul128(a,b,&result2_); + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "mulq %%rdx \n" + + : "=a" (result1_), "=d" (result2_) + : "0" (a), "1" (b) + : "cc" ); + + #endif + + + *result_low = result1_; + *result_high = result2_; + } + + + + + /*! + * + * Division + * + * + */ + + + /*! + this method calculates 64bits word a:b / 32bits c (a higher, b lower word) + r = a:b / c and rest - remainder + + ***this method is created only on a 64bit platform*** + + * + * WARNING: + * if r (one word) is too small for the result or c is equal zero + * there'll be a hardware interruption (0) + * and probably the end of your program + * + */ + template + void UInt::DivTwoWords(uint a,uint b, uint c, uint * r, uint * rest) + { + uint r_; + uint rest_; + /* + these variables have similar meaning like those in + the multiplication algorithm MulTwoWords + */ + + TTMATH_ASSERT( c != 0 ) + + + #ifndef __GNUC__ + + ttmath_div_x64(&a,&b,c); + r_ = a; + rest_ = b; + + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "divq %%rcx \n" + + : "=a" (r_), "=d" (rest_) + : "d" (a), "a" (b), "c" (c) + : "cc" ); + + #endif + + + *r = r_; + *rest = rest_; + } + +} //namespace + + +#endif //ifdef TTMATH_PLATFORM64 +#endif //ifndef TTMATH_NOASM +#endif + + diff --git a/extern/ttmath/ttmathuint_x86_64_msvc.asm b/extern/ttmath/ttmathuint_x86_64_msvc.asm new file mode 100644 index 0000000000..b7c85c2b84 --- /dev/null +++ b/extern/ttmath/ttmathuint_x86_64_msvc.asm @@ -0,0 +1,548 @@ +; +; This file is a part of TTMath Bignum Library +; and is distributed under the (new) BSD licence. +; Author: Christian Kaiser +; + +; +; Copyright (c) 2009, Christian Kaiser +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; * Redistributions of source code must retain the above copyright notice, +; this list of conditions and the following disclaimer. +; +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; * Neither the name Christian Kaiser nor the names of contributors to this +; project may be used to endorse or promote products derived +; from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +; THE POSSIBILITY OF SUCH DAMAGE. +; + +; +; compile with debug info: ml64.exe /c /Zd /Zi ttmathuint_x86_64_msvc.asm +; compile without debug info: ml64.exe /c ttmathuint_x86_64_msvc.asm +; this creates ttmathuint_x86_64_msvc.obj file which can be linked with your program +; + +PUBLIC ttmath_adc_x64 +PUBLIC ttmath_addindexed_x64 +PUBLIC ttmath_addindexed2_x64 +PUBLIC ttmath_addvector_x64 + +PUBLIC ttmath_sbb_x64 +PUBLIC ttmath_subindexed_x64 +PUBLIC ttmath_subvector_x64 + +PUBLIC ttmath_rcl_x64 +PUBLIC ttmath_rcr_x64 + +PUBLIC ttmath_rcl2_x64 +PUBLIC ttmath_rcr2_x64 + +PUBLIC ttmath_div_x64 + +; +; Microsoft x86_64 convention: http://msdn.microsoft.com/en-us/library/9b372w95.aspx +; +; "rax, rcx, rdx, r8-r11 are volatile." +; "rbx, rbp, rdi, rsi, r12-r15 are nonvolatile." +; + + +.CODE + + + ALIGN 8 + +;---------------------------------------- + +ttmath_adc_x64 PROC + ; rcx = p1 + ; rdx = p2 + ; r8 = nSize + ; r9 = nCarry + + xor rax, rax + xor r11, r11 + sub rax, r9 ; sets CARRY if r9 != 0 + + ALIGN 16 + loop1: + mov rax,qword ptr [rdx + r11 * 8] + adc qword ptr [rcx + r11 * 8], rax + lea r11, [r11+1] + dec r8 + jnz loop1 + + setc al + movzx rax, al + + ret + +ttmath_adc_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_addindexed_x64 PROC + + ; rcx = p1 + ; rdx = nSize + ; r8 = nPos + ; r9 = nValue + + xor rax, rax ; rax = result + sub rdx, r8 ; rdx = remaining count of uints + + add qword ptr [rcx + r8 * 8], r9 + jc next1 + + ret + +next1: + mov r9, 1 + + ALIGN 16 +loop1: + dec rdx + jz done_with_cy + lea r8, [r8+1] + add qword ptr [rcx + r8 * 8], r9 + jc loop1 + + ret + +done_with_cy: + lea rax, [rax+1] ; rax = 1 + + ret + +ttmath_addindexed_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_addindexed2_x64 PROC + + ; rcx = p1 (pointer) + ; rdx = b (value size) + ; r8 = nPos + ; r9 = nValue1 + ; [esp+0x28] = nValue2 + + xor rax, rax ; return value + mov r11, rcx ; table + sub rdx, r8 ; rdx = remaining count of uints + mov r10, [esp+028h] ; r10 = nValue2 + + add qword ptr [r11 + r8 * 8], r9 + lea r8, [r8+1] + lea rdx, [rdx-1] + adc qword ptr [r11 + r8 * 8], r10 + jc next + ret + + ALIGN 16 +loop1: + lea r8, [r8+1] + add qword ptr [r11 + r8 * 8], 1 + jc next + ret + +next: + dec rdx ; does not modify CY too... + jnz loop1 + lea rax, [rax+1] + ret + +ttmath_addindexed2_x64 ENDP + + + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + + +ttmath_addvector_x64 PROC + ; rcx = ss1 + ; rdx = ss2 + ; r8 = ss1_size + ; r9 = ss2_size + ; [esp+0x28] = result + + mov r10, [esp+028h] + sub r8, r9 + xor r11, r11 ; r11=0, cf=0 + + ALIGN 16 + loop1: + mov rax, qword ptr [rcx + r11 * 8] + adc rax, qword ptr [rdx + r11 * 8] + mov qword ptr [r10 + r11 * 8], rax + inc r11 + dec r9 + jnz loop1 + + adc r9, r9 ; r9 has the cf state + + or r8, r8 + jz done + + neg r9 ; setting cf from r9 + mov r9, 0 ; don't use xor here (cf is used) + loop2: + mov rax, qword ptr [rcx + r11 * 8] + adc rax, r9 + mov qword ptr [r10 + r11 * 8], rax + inc r11 + dec r8 + jnz loop2 + + adc r8, r8 + mov rax, r8 + + ret + +done: + mov rax, r9 + ret + +ttmath_addvector_x64 ENDP + + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_sbb_x64 PROC + + ; rcx = p1 + ; rdx = p2 + ; r8 = nCount + ; r9 = nCarry + + xor rax, rax + xor r11, r11 + sub rax, r9 ; sets CARRY if r9 != 0 + + ALIGN 16 + loop1: + mov rax,qword ptr [rdx + r11 * 8] + sbb qword ptr [rcx + r11 * 8], rax + lea r11, [r11+1] + dec r8 + jnz loop1 + + setc al + movzx rax, al + + ret + +ttmath_sbb_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_subindexed_x64 PROC + ; rcx = p1 + ; rdx = nSize + ; r8 = nPos + ; r9 = nValue + + sub rdx, r8 ; rdx = remaining count of uints + + ALIGN 16 +loop1: + sub qword ptr [rcx + r8 * 8], r9 + jnc done + + lea r8, [r8+1] + mov r9, 1 + dec rdx + jnz loop1 + + mov rax, 1 + ret + +done: + xor rax, rax + ret + +ttmath_subindexed_x64 ENDP + + + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +; the same asm code as in addvector_x64 only two instructions 'adc' changed to 'sbb' + +ttmath_subvector_x64 PROC + ; rcx = ss1 + ; rdx = ss2 + ; r8 = ss1_size + ; r9 = ss2_size + ; [esp+0x28] = result + + mov r10, [esp+028h] + sub r8, r9 + xor r11, r11 ; r11=0, cf=0 + + ALIGN 16 + loop1: + mov rax, qword ptr [rcx + r11 * 8] + sbb rax, qword ptr [rdx + r11 * 8] + mov qword ptr [r10 + r11 * 8], rax + inc r11 + dec r9 + jnz loop1 + + adc r9, r9 ; r9 has the cf state + + or r8, r8 + jz done + + neg r9 ; setting cf from r9 + mov r9, 0 ; don't use xor here (cf is used) + loop2: + mov rax, qword ptr [rcx + r11 * 8] + sbb rax, r9 + mov qword ptr [r10 + r11 * 8], rax + inc r11 + dec r8 + jnz loop2 + + adc r8, r8 + mov rax, r8 + + ret + +done: + mov rax, r9 + ret + +ttmath_subvector_x64 ENDP + + + + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_rcl_x64 PROC + ; rcx = p1 + ; rdx = b + ; r8 = nLowestBit + + mov r11, rcx ; table + xor r10, r10 + neg r8 ; CY set if r8 <> 0 + + ALIGN 16 +loop1: + rcl qword ptr [r11 + r10 * 8], 1 + lea r10, [r10+1] + dec rdx + jnz loop1 + + setc al + movzx rax, al + + ret + +ttmath_rcl_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_rcr_x64 PROC + ; rcx = p1 + ; rdx = nSize + ; r8 = nLowestBit + + xor r10, r10 + neg r8 ; CY set if r8 <> 0 + + ALIGN 16 +loop1: + rcr qword ptr -8[rcx + rdx * 8], 1 + dec rdx + jnz loop1 + + setc al + movzx rax, al + + ret + +ttmath_rcr_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_div_x64 PROC + + ; rcx = &Hi + ; rdx = &Lo + ; r8 = nDiv + + mov r11, rcx + mov r10, rdx + + mov rdx, qword ptr [r11] + mov rax, qword ptr [r10] + div r8 + mov qword ptr [r10], rdx ; remainder + mov qword ptr [r11], rax ; value + + ret + +ttmath_div_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_rcl2_x64 PROC + ; rcx = p1 + ; rdx = nSize + ; r8 = bits + ; r9 = c + + push rbx + + mov r10, rcx ; r10 = p1 + xor rax, rax + + mov rcx, 64 + sub rcx, r8 + + mov r11, -1 + shr r11, cl ; r11 = mask + + mov rcx, r8 ; rcx = count of bits + + mov rbx, rax ; rbx = old value = 0 + or r9, r9 + cmovnz rbx, r11 ; if (c) then old value = mask + + mov r9, rax ; r9 = index (0..nSize-1) + + ALIGN 16 +loop1: + rol qword ptr [r10+r9*8], cl + mov rax, qword ptr [r10+r9*8] + and rax, r11 + xor qword ptr [r10+r9*8], rax + or qword ptr [r10+r9*8], rbx + mov rbx, rax + + lea r9, [r9+1] + dec rdx + + jnz loop1 + + and rax, 1 + pop rbx + ret + +ttmath_rcl2_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_rcr2_x64 PROC + ; rcx = p1 + ; rdx = nSize + ; r8 = bits + ; r9 = c + + push rbx + mov r10, rcx ; r10 = p1 + xor rax, rax + + mov rcx, 64 + sub rcx, r8 + + mov r11, -1 + shl r11, cl ; r11 = mask + + mov rcx, r8 ; rcx = count of bits + + mov rbx, rax ; rbx = old value = 0 + or r9, r9 + cmovnz rbx, r11 ; if (c) then old value = mask + + mov r9, rdx ; r9 = index (0..nSize-1) + lea r9, [r9-1] + + ALIGN 16 +loop1: + ror qword ptr [r10+r9*8], cl + mov rax, qword ptr [r10+r9*8] + and rax, r11 + xor qword ptr [r10+r9*8], rax + or qword ptr [r10+r9*8], rbx + mov rbx, rax + + lea r9, [r9-1] + dec rdx + + jnz loop1 + + rol rax, 1 + and rax, 1 + pop rbx + + ret + +ttmath_rcr2_x64 ENDP + +END diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 43874a4c1d..b31f1383ed 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -1472,8 +1472,8 @@ void put_data(burp_rel* relation) add_byte(blr, field->fld_scale); break; - case blr_dec_fixed: - alignment = type_alignments[dtype_dec_fixed]; + case blr_int128: + alignment = type_alignments[dtype_int128]; add_byte(blr, field->fld_type); add_byte(blr, field->fld_scale); break; diff --git a/src/burp/canonical.cpp b/src/burp/canonical.cpp index ff7404de3f..7923d3de40 100644 --- a/src/burp/canonical.cpp +++ b/src/burp/canonical.cpp @@ -170,11 +170,15 @@ ULONG CAN_encode_decode(burp_rel* relation, lstring* buffer, UCHAR* data, bool d break; case dtype_dec128: - case dtype_dec_fixed: if (!xdr_dec128(xdrs, (Firebird::Decimal128*) p)) return FALSE; break; + case dtype_int128: + if (!xdr_int128(xdrs, (Firebird::Int128*) p)) + return FALSE; + break; + case dtype_timestamp: if (!xdr_long(xdrs, &((SLONG*) p)[0])) return FALSE; diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 7870ee7a0f..6f030b195b 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -3579,7 +3579,7 @@ rec_type get_data_old(BurpGlobals* tdgbl, burp_rel* relation) case blr_long: case blr_quad: case blr_int64: - case blr_dec_fixed: + case blr_int128: add_byte(blr, field->fld_type); add_byte(blr, field->fld_scale); break; diff --git a/src/common/DecFloat.cpp b/src/common/DecFloat.cpp index 9062bc68f5..75d330119a 100644 --- a/src/common/DecFloat.cpp +++ b/src/common/DecFloat.cpp @@ -28,6 +28,7 @@ #include "firebird.h" #include "DecFloat.h" +#include "Int128.h" #include "StatusArg.h" #include "gen/iberror.h" @@ -49,7 +50,7 @@ extern "C" using namespace Firebird; const DecimalStatus DecimalStatus::DEFAULT(FB_DEC_Errors); -const DecimalBinding DecimalBinding::DEFAULT; +const NumericBinding NumericBinding::DEFAULT; namespace { @@ -77,7 +78,7 @@ public: init(DEC_INIT_DECIMAL64); } - DecimalContext(const Decimal128Base*, DecimalStatus ds) + DecimalContext(const Decimal128*, DecimalStatus ds) : decSt(ds) { init(DEC_INIT_DECIMAL128); @@ -126,6 +127,8 @@ const CDecimal128 dmax(DBL_MAX, DecimalStatus(0)), dmin(-DBL_MAX, DecimalStatus( const CDecimal128 dzup(DBL_MIN, DecimalStatus(0)), dzlw(-DBL_MIN, DecimalStatus(0)); const CDecimal128 i64max(MAX_SINT64, DecimalStatus(0)), i64min(MIN_SINT64, DecimalStatus(0)); const CDecimal128 c1(1); +const CDecimal128 pow2_32("4294967296", DecimalStatus(0)); +const CDecimal128 pow2_64("18446744073709551616", DecimalStatus(0)); unsigned digits(const unsigned pMax, unsigned char* const coeff, int& exp) { @@ -265,7 +268,7 @@ Decimal64 Decimal64::set(SLONG value, DecimalStatus decSt, int scale) return *this; } -Decimal64 Decimal64::set(DecimalFixed value, DecimalStatus decSt, int scale) +Decimal64 Decimal64::set(Int128 value, DecimalStatus decSt, int scale) { Decimal128 tmp; tmp.set(value, decSt, scale); @@ -518,11 +521,21 @@ Decimal128 Decimal128::set(SLONG value, DecimalStatus decSt, int scale) return *this; } -Decimal128 Decimal128::set(DecimalFixed value, DecimalStatus decSt, int scale) +Decimal128 Decimal128::set(Int128 value, DecimalStatus decSt, int scale) { - *this = value; - setScale(decSt, -scale); + unsigned dwords[4]; + value.getTable32(dwords); + DecimalContext context(this, decSt); + decQuadFromInt32(&dec, dwords[3]); + for (int i = 3; i--; ) + { + decQuad dw; + decQuadFromUInt32(&dw, dwords[i]); + decQuadFMA(&dec, &dec, &pow2_32.dec, &dw, &context); + } + + setScale(decSt, -scale); return *this; } @@ -533,13 +546,10 @@ Decimal128 Decimal128::set(SINT64 value, DecimalStatus decSt, int scale) unsigned low = value & 0xFFFFFFFF; DecimalContext context(this, decSt); - decQuad pow2_32; - decQuadFromString(&pow2_32, "4294967296", &context); - decQuad up, down; decQuadFromInt32(&up, high); decQuadFromUInt32(&down, low); - decQuadFMA(&dec, &up, &pow2_32, &down, &context); + decQuadFMA(&dec, &up, &pow2_32.dec, &down, &context); } setScale(decSt, -scale); @@ -565,80 +575,6 @@ Decimal128 Decimal128::set(double value, DecimalStatus decSt) return *this; } -DecimalFixed DecimalFixed::set(SLONG value) -{ - decQuadFromInt32(&dec, value); - return *this; -} - -DecimalFixed DecimalFixed::set(SINT64 value) -{ - int high = value >> 32; - unsigned low = value & 0xFFFFFFFF; - - DecimalContext context(this, DecimalStatus(0)); - decQuad pow2_32; - decQuadFromString(&pow2_32, "4294967296", &context); - - decQuad up, down; - decQuadFromInt32(&up, high); - decQuadFromUInt32(&down, low); - decQuadFMA(&dec, &up, &pow2_32, &down, &context); - - return *this; -} - -DecimalFixed DecimalFixed::set(const char* value, int scale, DecimalStatus decSt) -{ - { // scope for 'context' - DecimalContext context(this, decSt); - decQuadFromString(&dec, value, &context); - } - - exactInt(decSt, scale); - return *this; -} - -DecimalFixed DecimalFixed::set(double value, int scale, DecimalStatus decSt) -{ - char s[50]; - sprintf(s, "%18.016e", value); - { // scope for 'context' - DecimalContext context(this, decSt); - decQuadFromString(&dec, s, &context); - } - - exactInt(decSt, scale); - return *this; -} - -void DecimalFixed::exactInt(DecimalStatus decSt, int scale) -{ - setScale(decSt, -scale); - - try - { - DecimalContext context(this, decSt); - decQuadToIntegralExact(&dec, &dec, &context); - decQuadQuantize(&dec, &dec, &c1.dec, &context); - } - catch (const Exception& ex) - { - FbLocalStatus st; - ex.stuffException(&st); - - switch (st->getErrors()[1]) - { - case isc_decfloat_invalid_operation: - (Arg::Gds(isc_decfloat_invalid_operation) << - Arg::Gds(isc_numeric_out_of_range)).raise(); - break; - } - - throw; - } -} - Decimal128 Decimal128::operator=(Decimal64 d64) { decDoubleToWider(&d64.dec, &dec); @@ -654,13 +590,6 @@ int Decimal128::toInteger(DecimalStatus decSt, int scale) const return decQuadToInt32(&tmp.dec, &context, rMode); } -int DecimalFixed::toInteger(DecimalStatus decSt) const -{ - DecimalContext context(this, decSt); - enum rounding rMode = decContextGetRounding(&context); - return decQuadToInt32(&dec, &context, rMode); -} - void Decimal128::toString(DecimalStatus decSt, unsigned length, char* to) const { DecimalContext context(this, decSt); @@ -690,24 +619,7 @@ void Decimal128::toString(string& to) const to.recalculate_length(); } -Decimal128 DecimalFixed::scaled128(DecimalStatus decSt, int scale) const -{ - Decimal128 tmp; - tmp.set(*this, decSt, -scale); - return tmp; -} - -void DecimalFixed::toString(DecimalStatus decSt, int scale, unsigned length, char* to) const -{ - scaled128(decSt, scale).toString(decSt, length, to); -} - -void DecimalFixed::toString(DecimalStatus decSt, int scale, string& to) const -{ - scaled128(decSt, scale).toString(to); -} - -double Decimal128Base::toDouble(DecimalStatus decSt) const +double Decimal128::toDouble(DecimalStatus decSt) const { DecimalContext context(this, decSt); @@ -764,37 +676,12 @@ SINT64 Decimal128::toInt64(DecimalStatus decSt, int scale) const return rc; } -SINT64 DecimalFixed::toInt64(DecimalStatus decSt) const -{ - if (compare(decSt, i64min) < 0 || compare(decSt, i64max) > 0) - { - DecimalContext context(this, decSt); - decContextSetStatus(&context, DEC_Invalid_operation); - return 0; // in case of no trap on invalid operation - } - - unsigned char coeff[DECQUAD_Pmax]; - int sign = decQuadGetCoefficient(&dec, coeff); - SINT64 rc = 0; - - for (int i = 0; i < DECQUAD_Pmax; ++i) - { - rc *= 10; - if (sign) - rc -= coeff[i]; - else - rc += coeff[i]; - } - - return rc; -} - -UCHAR* Decimal128Base::getBytes() +UCHAR* Decimal128::getBytes() { return dec.bytes; } -Decimal64 Decimal128Base::toDecimal64(DecimalStatus decSt) const +Decimal64 Decimal128::toDecimal64(DecimalStatus decSt) const { Decimal64 rc; DecimalContext context(this, decSt); @@ -802,7 +689,7 @@ Decimal64 Decimal128Base::toDecimal64(DecimalStatus decSt) const return rc; } -void Decimal128Base::setScale(DecimalStatus decSt, int scale) +void Decimal128::setScale(DecimalStatus decSt, int scale) { if (scale) { @@ -812,7 +699,7 @@ void Decimal128Base::setScale(DecimalStatus decSt, int scale) } } -int Decimal128Base::compare(DecimalStatus decSt, Decimal128Base tgt) const +int Decimal128::compare(DecimalStatus decSt, Decimal128 tgt) const { DecimalContext context(this, decSt); decQuad r; @@ -820,7 +707,7 @@ int Decimal128Base::compare(DecimalStatus decSt, Decimal128Base tgt) const return decQuadToInt32(&r, &context, DEC_ROUND_HALF_UP); } -bool Decimal128Base::isInf() const +bool Decimal128::isInf() const { switch(decQuadClass(&dec)) { @@ -832,7 +719,7 @@ bool Decimal128Base::isInf() const return false; } -bool Decimal128Base::isNan() const +bool Decimal128::isNan() const { switch(decQuadClass(&dec)) { @@ -844,7 +731,7 @@ bool Decimal128Base::isNan() const return false; } -int Decimal128Base::sign() const +int Decimal128::sign() const { if (decQuadIsZero(&dec)) return 0; @@ -870,7 +757,7 @@ Decimal128 Decimal128::floor(DecimalStatus decSt) const } #ifdef DEV_BUILD -int Decimal128Base::show() +int Decimal128::show() { decQuadShow(&dec, ""); return 0; @@ -915,50 +802,6 @@ Decimal128 Decimal128::mul(DecimalStatus decSt, Decimal128 op2) const return rc; } -DecimalFixed DecimalFixed::abs() const -{ - DecimalFixed rc; - decQuadCopyAbs(&rc.dec, &dec); - return rc; -} - -DecimalFixed DecimalFixed::neg() const -{ - DecimalFixed rc; - decQuadCopyNegate(&rc.dec, &dec); - return rc; -} - -DecimalFixed DecimalFixed::add(DecimalStatus decSt, DecimalFixed op2) const -{ - DecimalContext context(this, decSt); - DecimalFixed rc; - decQuadAdd(&rc.dec, &dec, &op2.dec, &context); - context.checkForExceptions(); - decQuadQuantize(&rc.dec, &rc.dec, &c1.dec, &context); - return rc; -} - -DecimalFixed DecimalFixed::sub(DecimalStatus decSt, DecimalFixed op2) const -{ - DecimalContext context(this, decSt); - DecimalFixed rc; - decQuadSubtract(&rc.dec, &dec, &op2.dec, &context); - context.checkForExceptions(); - decQuadQuantize(&rc.dec, &rc.dec, &c1.dec, &context); - return rc; -} - -DecimalFixed DecimalFixed::mul(DecimalStatus decSt, DecimalFixed op2) const -{ - DecimalContext context(this, decSt); - DecimalFixed rc; - decQuadMultiply(&rc.dec, &dec, &op2.dec, &context); - context.checkForExceptions(); - decQuadQuantize(&rc.dec, &rc.dec, &c1.dec, &context); - return rc; -} - Decimal128 Decimal128::div(DecimalStatus decSt, Decimal128 op2) const { DecimalContext context(this, decSt); @@ -967,27 +810,6 @@ Decimal128 Decimal128::div(DecimalStatus decSt, Decimal128 op2) const return rc; } -DecimalFixed DecimalFixed::div(DecimalStatus decSt, DecimalFixed op2, int scale) const -{ - DecimalContext context(this, decSt); - DecimalFixed rc; - - // first divide with full decfloat precision - decQuadDivide(&rc.dec, &dec, &op2.dec, &context); - - // next re-scale & int-ize - rc.exactInt(decSt, scale); - return rc; -} - -DecimalFixed DecimalFixed::mod(DecimalStatus decSt, DecimalFixed op2) const -{ - DecimalContext context(this, decSt); - DecimalFixed rc; - decQuadRemainder(&rc.dec, &dec, &op2.dec, &context); - return rc; -} - Decimal128 Decimal128::fma(DecimalStatus decSt, Decimal128 op2, Decimal128 op3) const { DecimalContext context(this, decSt); @@ -1049,7 +871,7 @@ Decimal128 Decimal128::log10(DecimalStatus decSt) const return rc; } -void Decimal128Base::makeKey(ULONG* key) const +void Decimal128::makeKey(ULONG* key) const { unsigned char coeff[DECQUAD_Pmax]; int sign = decQuadGetCoefficient(&dec, coeff); @@ -1058,7 +880,7 @@ void Decimal128Base::makeKey(ULONG* key) const make(key, DECQUAD_Pmax, DECQUAD_Bias, sizeof(dec), coeff, sign, exp); } -void Decimal128Base::grabKey(ULONG* key) +void Decimal128::grabKey(ULONG* key) { int exp, sign; unsigned char bcd[DECQUAD_Pmax]; @@ -1068,12 +890,12 @@ void Decimal128Base::grabKey(ULONG* key) decQuadFromBCD(&dec, exp, bcd, sign); } -ULONG Decimal128Base::getIndexKeyLength() +ULONG Decimal128::getIndexKeyLength() { return 17; } -ULONG Decimal128Base::makeIndexKey(vary* buf) +ULONG Decimal128::makeIndexKey(vary* buf) { unsigned char coeff[DECQUAD_Pmax + 2]; int sign = decQuadGetCoefficient(&dec, coeff); @@ -1207,4 +1029,9 @@ short Decimal128::decCompare(Decimal128 op2) const return 3; } +void Decimal128::getBcd(BCD* bcd) const +{ + bcd->sign = decQuadToBCD(&dec, &bcd->exp, bcd->bcd); +} + } // namespace Firebird diff --git a/src/common/DecFloat.h b/src/common/DecFloat.h index 9cdabdfe2f..bf14abd3ab 100644 --- a/src/common/DecFloat.h +++ b/src/common/DecFloat.h @@ -115,27 +115,27 @@ struct DecimalStatus USHORT decExtFlag, roundingMode; }; -struct DecimalBinding +struct NumericBinding { enum Bind { - DEC_NATIVE, - DEC_TEXT, - DEC_DOUBLE, - DEC_NUMERIC + NUM_NATIVE, + NUM_TEXT, + NUM_DOUBLE, + NUM_INT64 }; - DecimalBinding() - : bind(DEC_NATIVE), + NumericBinding() + : bind(NUM_NATIVE), numScale(0) {} - DecimalBinding(Bind aBind, SCHAR aNumScale = 0) + NumericBinding(Bind aBind, SCHAR aNumScale = 0) : bind(aBind), numScale(aNumScale) {} - static const DecimalBinding DEFAULT; + static const NumericBinding DEFAULT; static const SCHAR MAX_SCALE = 18; Bind bind; @@ -143,13 +143,11 @@ struct DecimalBinding }; -class DecimalFixed; +class Int128; class Decimal64 { friend class Decimal128; - friend class DecimalFixed; - friend class Decimal128Base; public: #if SIZEOF_LONG < 8 @@ -159,7 +157,7 @@ public: Decimal64 set(SINT64 value, DecimalStatus decSt, int scale); Decimal64 set(const char* value, DecimalStatus decSt); Decimal64 set(double value, DecimalStatus decSt); - Decimal64 set(DecimalFixed value, DecimalStatus decSt, int scale); + Decimal64 set(Int128 value, DecimalStatus decSt, int scale); UCHAR* getBytes(); Decimal64 abs() const; @@ -193,38 +191,7 @@ private: decDouble dec; }; -class Decimal128Base -{ - friend class Decimal128; - friend class DecimalFixed; - -public: - double toDouble(DecimalStatus decSt) const; - Decimal64 toDecimal64(DecimalStatus decSt) const; - - UCHAR* getBytes(); - int compare(DecimalStatus decSt, Decimal128Base tgt) const; - - void setScale(DecimalStatus decSt, int scale); - - bool isInf() const; - bool isNan() const; - int sign() const; - - void makeKey(ULONG* key) const; - void grabKey(ULONG* key); - static ULONG getIndexKeyLength(); - ULONG makeIndexKey(vary* buf); - -#ifdef DEV_BUILD - int show(); -#endif - -private: - decQuad dec; -}; - -class Decimal128 : public Decimal128Base +class Decimal128 { friend class Decimal64; @@ -237,7 +204,7 @@ public: Decimal128 set(SINT64 value, DecimalStatus decSt, int scale); Decimal128 set(const char* value, DecimalStatus decSt); Decimal128 set(double value, DecimalStatus decSt); - Decimal128 set(DecimalFixed value, DecimalStatus decSt, int scale); + Decimal128 set(Int128 value, DecimalStatus decSt, int scale); Decimal128 operator=(Decimal64 d64); @@ -265,12 +232,37 @@ public: short totalOrder(Decimal128 op2) const; short decCompare(Decimal128 op2) const; -private: - Decimal128 operator=(Decimal128Base d128b) + double toDouble(DecimalStatus decSt) const; + Decimal64 toDecimal64(DecimalStatus decSt) const; + + UCHAR* getBytes(); + int compare(DecimalStatus decSt, Decimal128 tgt) const; + + void setScale(DecimalStatus decSt, int scale); + + bool isInf() const; + bool isNan() const; + int sign() const; + + void makeKey(ULONG* key) const; + void grabKey(ULONG* key); + static ULONG getIndexKeyLength(); + ULONG makeIndexKey(vary* buf); + +#ifdef DEV_BUILD + int show(); +#endif + + struct BCD { - memcpy(&dec, &d128b.dec, sizeof(dec)); - return *this; - } + int sign, exp; + unsigned char bcd[DECQUAD_Pmax]; + }; + + void getBcd(BCD* bcd) const; + +private: + decQuad dec; }; class CDecimal128 : public Decimal128 @@ -290,45 +282,11 @@ public: { set(value, DecimalStatus(0), 0); } -}; -class DecimalFixed : public Decimal128Base -{ -public: -#if SIZEOF_LONG < 8 - DecimalFixed set(int value) + CDecimal128(const char* value, DecimalStatus decSt) { - return set(SLONG(value)); + set(value, decSt); } -#endif - DecimalFixed set(SLONG value); - DecimalFixed set(SINT64 value); - DecimalFixed set(const char* value, int scale, DecimalStatus decSt); - DecimalFixed set(double value, int scale, DecimalStatus decSt); - - int toInteger(DecimalStatus decSt) const; - SINT64 toInt64(DecimalStatus decSt) const; - void toString(DecimalStatus decSt, int scale, unsigned length, char* to) const; - void toString(DecimalStatus decSt, int scale, string& to) const; - - DecimalFixed abs() const; - DecimalFixed neg() const; - DecimalFixed add(DecimalStatus decSt, DecimalFixed op2) const; - DecimalFixed sub(DecimalStatus decSt, DecimalFixed op2) const; - DecimalFixed mul(DecimalStatus decSt, DecimalFixed op2) const; - DecimalFixed div(DecimalStatus decSt, DecimalFixed op2, int scale) const; - DecimalFixed mod(DecimalStatus decSt, DecimalFixed op2) const; - - DecimalFixed operator=(Decimal128Base d128b) - { - memcpy(&dec, &d128b.dec, sizeof(dec)); - return *this; - } - - void exactInt(DecimalStatus decSt, int scale); // rescale & make it integer after conversions - -private: - Decimal128 scaled128(DecimalStatus decSt, int scale) const; }; } // namespace Firebird diff --git a/src/common/Int128.cpp b/src/common/Int128.cpp new file mode 100644 index 0000000000..001e00534d --- /dev/null +++ b/src/common/Int128.cpp @@ -0,0 +1,492 @@ +/* + * PROGRAM: Integer 128 type. + * MODULE: Int128.cpp + * DESCRIPTION: Big integer support. + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alex Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2019 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#include "firebird.h" +#include "Int128.h" + +#include "StatusArg.h" +#include "gen/iberror.h" +#include "status.h" +#include "DecFloat.h" + +#include + +#include +#include +#include + +using namespace Firebird; + +namespace { + +const CInt128 i64max(MAX_SINT64), i64min(MIN_SINT64); +const double p2_32 = 4294967296.0; +const I128limit i128limit; +const CInt128 minus1(-1); + + +} // anonymous namespace + + + +namespace Firebird { + +Int128 Int128::set(SLONG value, int scale) +{ + v = value; + setScale(scale); + return *this; +} + +Int128 Int128::set(SINT64 value, int scale) +{ +#ifdef TTMATH_PLATFORM32 + v = ttmath::slint(value); +#else + v = ttmath::sint(value); +#endif + setScale(scale); + return *this; +} + +Int128 Int128::set(const char* value) +{ +// This is simplified method - it does not perform all what's needed for CVT_decompose + v.FromString(value); + return *this; +} + +Int128 Int128::set(double value) +{ + bool sgn = false; + if (value < 0.0) + { + value = -value; + sgn = true; + } + + double parts[4]; + for (int i = 0; i < 4; ++i) + { + parts[i] = value; + value /= p2_32; + } + fb_assert(value < 1.0); + + unsigned dwords[4]; + value = 0.0; + for (int i = 4; i--;) + { + dwords[i] = (parts[i] - value); + value += p2_32 * dwords[i]; + } + + setTable32(dwords); + if (sgn) + v.ChangeSign(); + + return *this; +} + +Int128 Int128::set(DecimalStatus decSt, Decimal128 value) +{ + static CDecimal128 quant(1); + value = value.quantize(decSt, quant); + + Decimal128::BCD bcd; + value.getBcd(&bcd); + fb_assert(bcd.exp == 0); + + v.SetZero(); + for (unsigned b = 0; b < sizeof(bcd.bcd); ++b) + { + v.MulInt(10); + v.AddInt(bcd.bcd[b]); + } + if (bcd.sign < 0) + v.ChangeSign(); + + return *this; +} + +void Int128::setScale(int scale) +{ + if (scale > 0) + { + ttmath::sint rem = 0; + while (scale--) + v.DivInt(10, scale == 0 ? &rem : nullptr); + + if (rem > 4) + v++; + else if (rem < -4) + v--; + } + else if (scale < 0) + { + while (scale++) { + if (v > i128limit.v || v < -i128limit.v) + (Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)).raise(); + v.MulInt(10); + } + } +} + +int Int128::toInteger(int scale) const +{ + Int128 tmp(*this); + tmp.setScale(scale); + int rc; + if (tmp.v.ToInt(rc)) + overflow(); + return rc; +} + +void Int128::toString(int scale, unsigned length, char* to) const +{ + string buffer; + toString(scale, buffer); + if (buffer.length() + 1 > length) + { + (Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) << + Arg::Gds(isc_trunc_limits) << Arg::Num(length) << Arg::Num(buffer.length() + 1)).raise(); + } + buffer.copyTo(to, length); +} + +void Int128::toString(int scale, string& to) const +{ + v.ToStringBase(to); + bool sgn = to[0] == '-'; + if (sgn) + to.erase(0, 1); + + if (scale) + { + if (scale < -38 || scale > 4) + { + string tmp; + tmp.printf("E%d", scale); + to += tmp; + } + else if (scale > 0) + { + string tmp(scale, '0'); + to += tmp; + } + else + { + unsigned posScale = -scale; + if (posScale > to.length()) + { + string tmp(posScale - to.length(), '0'); + to.insert(0, tmp); + } + if (posScale == to.length()) + { + to.insert(0, "0."); + } + else + to.insert(to.length() - posScale, "."); + } + } + + if (sgn) + to.insert(0, "-"); +} + +SINT64 Int128::toInt64(int scale) const +{ + Int128 tmp(*this); + tmp.setScale(scale); + if (tmp.v < i64min.v || tmp.v > i64max.v) + overflow(); + + unsigned dwords[4]; + tmp.getTable32(dwords); + SINT64 rc = int(dwords[1]); + rc <<= 32; + rc += dwords[0]; + + return rc; +} + +double Int128::toDouble() const +{ + unsigned dwords[4]; + getTable32(dwords); + double rc = int(dwords[3]); + for (int i = 3; i--;) + { + rc *= p2_32; + rc += dwords[i]; + } + + return rc; +} + +int Int128::compare(Int128 tgt) const +{ + return v < tgt.v ? -1 : v > tgt.v ? 1 : 0; +} + +Int128 Int128::abs() const +{ + Int128 rc(*this); + if (rc.v.Abs()) + overflow(); + return rc; +} + +Int128 Int128::neg() const +{ + Int128 rc(*this); + if (rc.v.ChangeSign()) + overflow(); + return rc; +} + +Int128 Int128::add(Int128 op2) const +{ + Int128 rc(*this); + if (rc.v.Add(op2.v)) + overflow(); + return rc; +} + +Int128 Int128::sub(Int128 op2) const +{ + Int128 rc(*this); + if (rc.v.Sub(op2.v)) + overflow(); + return rc; +} + +Int128 Int128::mul(Int128 op2) const +{ + Int128 rc(*this); + if (rc.v.Mul(op2.v)) + overflow(); + return rc; +} + +Int128 Int128::div(Int128 op2, int scale) const +{ + if (compare(MIN_Int128) == 0 && op2.compare(minus1) == 0) + Arg::Gds(isc_exception_integer_overflow).raise(); + + static const CInt128 MIN_BY10(MIN_Int128 / 10); + static const CInt128 MAX_BY10(MAX_Int128 / 10); + + // Scale op1 by as many of the needed powers of 10 as possible without an overflow. + CInt128 op1(*this); + int sign1 = op1.sign(); + while ((scale < 0) && (sign1 >= 0 ? op1.compare(MAX_BY10) <= 0 : op1.compare(MIN_BY10) >= 0)) + { + op1 *= 10; + ++scale; + } + + // Scale op2 shifting it to the right as long as only zeroes are thrown away. + CInt128 tmp(op2); + while (scale < 0) + { + ttmath::sint rem = 0; + tmp.v.DivInt(10, &rem); + if (rem) + break; + op2 = tmp; + ++scale; + } + + if (op1.v.Div(op2.v)) + zerodivide(); + + op1.setScale(scale); + return op1; +} + +Int128 Int128::mod(Int128 op2) const +{ + Int128 tmp(*this); + Int128 rc; + if (tmp.v.Div(op2.v, rc.v)) + zerodivide(); + return rc; +} + +int Int128::sign() const +{ + return v.IsSign() ? -1 : v.IsZero() ? 0 : 1; +} + +UCHAR* Int128::getBytes() +{ + return (UCHAR*)(v.table); +} + +void Int128::getTable32(unsigned* dwords) const +{ + static_assert((sizeof(v.table[0]) == 4) || (sizeof(v.table[0]) == 8), + "Unsupported size of integer in ttmath"); + + if (sizeof(v.table[0]) == 4) + { + for (int i = 0; i < 4; ++i) + dwords[i] = v.table[i]; + } + else if (sizeof(v.table[0]) == 8) + { + for (int i = 0; i < 2; ++i) + { + dwords[i * 2] = v.table[i] & 0xFFFFFFFF; + dwords[i * 2 + 1] = (v.table[i] >> 32) & 0xFFFFFFFF; + } + } +} + +void Int128::setTable32(const unsigned* dwords) +{ + static_assert((sizeof(v.table[0]) == 4) || (sizeof(v.table[0]) == 8), + "Unsupported size of integer in ttmath"); + + if (sizeof(v.table[0]) == 4) + { + for (int i = 0; i < 4; ++i) + v.table[i] = dwords[i]; + } + else if (sizeof(v.table[0]) == 8) + { + for (int i = 0; i < 2; ++i) + { + v.table[i] = dwords[i * 2 + 1]; + v.table[i] <<= 32; + v.table[i] += dwords[i * 2]; + } + } +} + +Int128 Int128::operator&=(FB_UINT64 mask) +{ + v.table[0] &= mask; + unsigned i = 1; + if (sizeof(v.table[0]) == 4) + { + i = 2; + v.table[1] &= (mask >> 32); + } + + for (; i < FB_NELEM(v.table); ++i) + v.table[i] = 0; + return *this; +} + +Int128 Int128::operator&=(ULONG mask) +{ + v.table[0] &= mask; + + for (unsigned i = 1; i < FB_NELEM(v.table); ++i) + v.table[i] = 0; + return *this; +} + +Int128 Int128::operator/(unsigned value) const +{ + Int128 rc; + rc.v.DivInt(value); + return rc; +} + +Int128 Int128::operator-() const +{ + return neg(); +} + +Int128 Int128::operator+=(unsigned value) +{ + v.AddInt(value); + return *this; +} + +Int128 Int128::operator*=(unsigned value) +{ + v.MulInt(value); + return *this; +} + +bool Int128::operator>(Int128 value) const +{ + return v > value.v; +} + +bool Int128::operator==(Int128 value) const +{ + return v == value.v; +} + +void Int128::zerodivide() +{ + (Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_integer_divide_by_zero)).raise(); +} + +void Int128::overflow() +{ + (Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_integer_overflow)).raise(); +} + +#ifdef DEV_BUILD +const char* Int128::show() +{ + static char to[64]; + toString(0, sizeof(to), to); + return to; +} +#endif + +CInt128::CInt128(SINT64 value) +{ + set(value, 0); +} + +CInt128::CInt128(minmax mm) +{ + switch(mm) + { + case MkMax: + v.SetMax(); + break; + case MkMin: + v.SetMin(); + break; + } +} + +CInt128 MIN_Int128(CInt128::MkMin); +CInt128 MAX_Int128(CInt128::MkMax); + +} // namespace Firebird diff --git a/src/common/Int128.h b/src/common/Int128.h new file mode 100644 index 0000000000..758e387174 --- /dev/null +++ b/src/common/Int128.h @@ -0,0 +1,147 @@ +/* + * PROGRAM: Integer 128 type. + * MODULE: Int128.h + * DESCRIPTION: Big integer support. + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alex Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2019 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#ifndef FB_INT128 +#define FB_INT128 + +#include "firebird/Interface.h" +#include "fb_exception.h" + +#include + +#include "classes/fb_string.h" +#include "classes/MetaName.h" + +#include "../../extern/ttmath/ttmath.h" + +namespace Firebird { + +class Decimal64; +class Decimal128; +struct DecimalStatus; + +class Int128 //: public Decimal128Base +{ +public: +#if SIZEOF_LONG < 8 + Int128 set(int value) + { + return set(SLONG(value)); + } +#endif + Int128 set(SLONG value, int scale); + Int128 set(SINT64 value, int scale); + Int128 set(double value); + Int128 set(DecimalStatus decSt, Decimal128 value); + Int128 set(Int128 value) + { + v = value.v; + return *this; + } + + Int128 operator=(SINT64 value) + { + set(value, 0); + return *this; + } + +#ifdef DEV_BUILD + const char* show(); +#endif + + int toInteger(int scale) const; + SINT64 toInt64(int scale) const; + void toString(int scale, unsigned length, char* to) const; + void toString(int scale, string& to) const; + double toDouble() const; + + Int128 operator&=(FB_UINT64 mask); + Int128 operator&=(ULONG mask); + Int128 operator-() const; + Int128 operator/(unsigned value) const; + Int128 operator+=(unsigned value); + Int128 operator*=(unsigned value); + + int compare(Int128 tgt) const; + bool operator>(Int128 value) const; + bool operator==(Int128 value) const; + int sign() const; + + Int128 abs() const; + Int128 neg() const; + Int128 add(Int128 op2) const; + Int128 sub(Int128 op2) const; + Int128 mul(Int128 op2) const; + Int128 div(Int128 op2, int scale) const; + Int128 mod(Int128 op2) const; + + void getTable32(unsigned* dwords) const; // internal data in per-32bit form + void setTable32(const unsigned* dwords); + void setScale(int scale); + UCHAR* getBytes(); + +protected: + ttmath::Int v; + + static void overflow(); + static void zerodivide(); + + Int128 set(const char* value); +}; + +class CInt128 : public Int128 +{ +public: + enum minmax {MkMax, MkMin}; + + CInt128(SINT64 value); + CInt128(minmax mm); + CInt128(const Int128& value) + { + set(value); + } +}; + +extern CInt128 MAX_Int128, MIN_Int128; + +class I128limit : public Int128 +{ +public: + I128limit() + { + v.SetOne(); + for (int i = 0; i < 126; ++i) + v.MulInt(2); + v.DivInt(5); + } +}; + +} // namespace Firebird + + +#endif // FB_INT128 diff --git a/src/common/classes/InternalMessageBuffer.cpp b/src/common/classes/InternalMessageBuffer.cpp index fbd153763d..45c5a44a86 100644 --- a/src/common/classes/InternalMessageBuffer.cpp +++ b/src/common/classes/InternalMessageBuffer.cpp @@ -192,9 +192,9 @@ MetadataFromBlr::MetadataFromBlr(unsigned aBlrLength, const unsigned char* aBlr, item->length = sizeof(Decimal128); break; - case blr_dec_fixed: - item->type = SQL_DEC_FIXED; - item->length = sizeof(DecimalFixed); + case blr_int128: + item->type = SQL_INT128; + item->length = sizeof(Int128); item->scale = rdr.getByte(); break; diff --git a/src/common/classes/fb_string.h b/src/common/classes/fb_string.h index f9d6c1b5ca..b759c1f82a 100644 --- a/src/common/classes/fb_string.h +++ b/src/common/classes/fb_string.h @@ -504,7 +504,10 @@ namespace Firebird memset(baseInsert(p0, n), c, n); return *this; } - // iterator insert(iterator it, char_type c); // what to return here? + void insert(iterator it, char_type c) + { + insert(it - c_str(), 1, c); + } void insert(iterator it, size_type n, char_type c) { insert(it - c_str(), n, c); @@ -519,6 +522,11 @@ namespace Firebird baseErase(p0, n); return *this; } + AbstractString& clear() throw() + { + erase(); + return *this; + } iterator erase(iterator it) throw() { erase(it - c_str(), 1); diff --git a/src/common/cvt.cpp b/src/common/cvt.cpp index 76f7acca82..debc9cff6f 100644 --- a/src/common/cvt.cpp +++ b/src/common/cvt.cpp @@ -133,7 +133,7 @@ static void datetime_to_text(const dsc*, dsc*, Callbacks*); static void float_to_text(const dsc*, dsc*, Callbacks*); static void decimal_float_to_text(const dsc*, dsc*, DecimalStatus, Callbacks*); static void integer_to_text(const dsc*, dsc*, Callbacks*); -static SINT64 hex_to_value(const char*& string, const char* end); +static void int128_to_text(const dsc*, dsc*, Callbacks* cb); static void localError(const Firebird::Arg::StatusVector&); class DummyException {}; @@ -305,7 +305,35 @@ static void decimal_float_to_text(const dsc* from, dsc* to, DecimalStatus decSt, else if (from->dsc_dtype == dtype_dec128) ((Decimal128*) from->dsc_address)->toString(decSt, sizeof(temp), temp); else - ((DecimalFixed*) from->dsc_address)->toString(decSt, from->dsc_scale, sizeof(temp), temp); + fb_assert(false); + } + catch (const Exception& ex) + { + // reraise using function passed in callbacks + Arg::StatusVector v(ex); + cb->err(v); + } + + dsc intermediate; + intermediate.dsc_dtype = dtype_text; + intermediate.dsc_ttype() = ttype_ascii; + intermediate.dsc_address = reinterpret_cast(temp); + intermediate.dsc_length = strlen(temp); + + CVT_move_common(&intermediate, to, 0, cb); +} + + +static void int128_to_text(const dsc* from, dsc* to, Callbacks* cb) +{ + char temp[50]; + + try + { + if (from->dsc_dtype == dtype_int128) + ((Int128*) from->dsc_address)->toString(from->dsc_scale, sizeof(temp), temp); + else + fb_assert(false); } catch (const Exception& ex) { @@ -1070,9 +1098,8 @@ SLONG CVT_get_long(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunc return d128.toInteger(decSt, scale); - case dtype_dec_fixed: - value = ((DecimalFixed*) p)->toInteger(decSt); - break; + case dtype_int128: + return ((Int128*) p)->toInteger(scale); case dtype_real: case dtype_double: @@ -1121,7 +1148,7 @@ SLONG CVT_get_long(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunc { USHORT length = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err); - scale -= CVT_decompose(p, length, dtype_long, &value, err); + scale -= CVT_decompose(p, length, &value, err); } break; @@ -1274,8 +1301,8 @@ double CVT_get_double(const dsc* desc, DecimalStatus decSt, ErrorFunction err, b return d128.toDouble(decSt); } - case dtype_dec_fixed: - value = ((DecimalFixed*) desc->dsc_address)->toDouble(decSt); + case dtype_int128: + value = ((Int128*) desc->dsc_address)->toDouble(); break; case dtype_varying: @@ -1578,7 +1605,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c case dtype_boolean: case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: CVT_conversion_error(from, cb->err); break; } @@ -1629,7 +1656,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c case dtype_boolean: case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: CVT_conversion_error(from, cb->err); break; } @@ -1671,7 +1698,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c case dtype_boolean: case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: CVT_conversion_error(from, cb->err); break; } @@ -1716,7 +1743,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c case dtype_boolean: case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: CVT_conversion_error(from, cb->err); break; } @@ -1763,7 +1790,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c case dtype_boolean: case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: CVT_conversion_error(from, cb->err); break; } @@ -1946,6 +1973,10 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c integer_to_text(from, to, cb); return; + case dtype_int128: + int128_to_text(from, to, cb); + return; + case dtype_real: case dtype_double: float_to_text(from, to, cb); @@ -1953,7 +1984,6 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: decimal_float_to_text(from, to, decSt, cb); return; @@ -2082,8 +2112,8 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c *((Decimal128*) p) = CVT_get_dec128(from, decSt, cb->err); return; - case dtype_dec_fixed: - *((DecimalFixed*) p) = CVT_get_dec_fixed(from, (SSHORT) to->dsc_scale, decSt, cb->err); + case dtype_int128: + *((Int128*) p) = CVT_get_int128(from, (SSHORT) to->dsc_scale, decSt, cb->err); return; case dtype_boolean: @@ -2108,7 +2138,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c case dtype_double: case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: CVT_conversion_error(from, cb->err); break; } @@ -2494,11 +2524,28 @@ double CVT_power_of_ten(const int scale) } -SSHORT CVT_decompose(const char* string, - USHORT length, - SSHORT dtype, - SLONG* return_value, - ErrorFunction err) +class RetPtr +{ +public: + virtual ~RetPtr() { } + + enum lb10 {RETVAL_OVERFLOW, RETVAL_POSSIBLE_OVERFLOW, RETVAL_NO_OVERFLOW}; + + virtual USHORT maxSize() = 0; + virtual void truncate8() = 0; + virtual void truncate16() = 0; + virtual lb10 compareLimitBy10() = 0; + virtual void nextDigit(unsigned digit, unsigned base) = 0; + virtual bool isLowerLimit() = 0; + virtual void neg() = 0; +}; + +static void hex_to_value(const char*& string, const char* end, RetPtr* retValue); + +static SSHORT cvt_decompose(const char* string, + USHORT length, + RetPtr* return_value, + ErrorFunction err) { /************************************** * @@ -2512,15 +2559,6 @@ SSHORT CVT_decompose(const char* string, * **************************************/ - // For now, this routine does not handle quadwords unless this is - // supported by the platform as a native datatype. - - if (dtype == dtype_quad) - { - fb_assert(false); - err(Arg::Gds(isc_badblk)); // internal error - } - dsc errd; MOVE_CLEAR(&errd, sizeof(errd)); errd.dsc_dtype = dtype_text; @@ -2528,14 +2566,9 @@ SSHORT CVT_decompose(const char* string, errd.dsc_length = length; errd.dsc_address = reinterpret_cast(const_cast(string)); - SINT64 value = 0; SSHORT scale = 0; int sign = 0; bool digit_seen = false, fraction = false; - const SINT64 lower_limit = (dtype == dtype_long) ? MIN_SLONG : MIN_SINT64; - const SINT64 upper_limit = (dtype == dtype_long) ? MAX_SLONG : MAX_SINT64; - - const SINT64 limit_by_10 = upper_limit / 10; // used to check for overflow const char* p = string; const char* end = p + length; @@ -2561,28 +2594,20 @@ SSHORT CVT_decompose(const char* string, while (q < end && *q == ' ') q++; - if (q != end || end - p == 0 || end - p > 16) + if (q != end || end - p == 0 || end - p > return_value->maxSize()) CVT_conversion_error(&errd, err); q = p; - value = hex_to_value(q, digits_end); + hex_to_value(q, digits_end, return_value); if (q != digits_end) CVT_conversion_error(&errd, err); // 0xFFFFFFFF = -1; 0x0FFFFFFFF = 4294967295 if (digits_end - p <= 8) - value = (SLONG) value; - - if (dtype == dtype_long) - { - if (value < LONG_MIN_int64 || value > LONG_MAX_int64) - err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); - - *return_value = (SLONG) value; - } - else - *((SINT64*) return_value) = value; + return_value->truncate8(); + else if (digits_end - p <= 16) + return_value->truncate16(); return 0; // 0 scale for hex literals } @@ -2598,20 +2623,33 @@ SSHORT CVT_decompose(const char* string, // tricky: the value doesn't always become negative after an // overflow! - if (value >= limit_by_10) + switch(return_value->compareLimitBy10()) { - // possibility of an overflow - if (value > limit_by_10) - { - err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); - } - else if ((*p > '8' && sign == -1) || (*p > '7' && sign != -1)) + case RetPtr::RETVAL_OVERFLOW: + if (fraction) + { + while (p < end) + { + if (*p != '0') + break; + ++p; + } + if (p >= end) + continue; + } + err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + break; + case RetPtr::RETVAL_POSSIBLE_OVERFLOW: + if ((*p > '8' && sign == -1) || (*p > '7' && sign != -1)) { err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); } + break; + default: + break; } - value = value * 10 + *p - '0'; + return_value->nextDigit(*p - '0', 10); if (fraction) --scale; } @@ -2645,8 +2683,8 @@ SSHORT CVT_decompose(const char* string, if (!digit_seen) CVT_conversion_error(&errd, err); - if ((sign == -1) && value != lower_limit) - value = -value; + if ((sign == -1) && !return_value->isLowerLimit()) + return_value->neg(); // If there's still something left, there must be an explicit exponent if (p < end) @@ -2693,18 +2731,160 @@ SSHORT CVT_decompose(const char* string, if (!digit_seen) CVT_conversion_error(&errd, err); - } - if (dtype == dtype_long) - *return_value = (SLONG) value; - else - *((SINT64 *) return_value) = value; - return scale; } +template +class RetValue : public RetPtr +{ +public: + RetValue(typename Traits::ValueType* ptr) + : return_value(ptr) + { + value = 0; + } + + ~RetValue() + { + *return_value = value; + } + + USHORT maxSize() + { + return sizeof(typename Traits::ValueType); + } + + void truncate8() + { + ULONG mask = 0xFFFFFFFF; + value &= mask; + } + + void truncate16() + { + FB_UINT64 mask = 0xFFFFFFFFFFFFFFFF; + value &= mask; + } + + lb10 compareLimitBy10() + { + if (value > Traits::UPPER_LIMIT / 10) + return RETVAL_OVERFLOW; + if (value == Traits::UPPER_LIMIT / 10) + return RETVAL_POSSIBLE_OVERFLOW; + return RETVAL_NO_OVERFLOW; + } + + void nextDigit(unsigned digit, unsigned base) + { + value *= base; + value += digit; + } + + bool isLowerLimit() + { + return value == Traits::LOWER_LIMIT; + } + + void neg() + { + value = -value; + } + +private: + typename Traits::ValueType value; + typename Traits::ValueType* return_value; +}; + + +class SLONGTraits +{ +public: + typedef SLONG ValueType; + static const SLONG UPPER_LIMIT = MAX_SLONG; + static const SLONG LOWER_LIMIT = MIN_SLONG; +}; + +SSHORT CVT_decompose(const char* str, USHORT len, SLONG* val, ErrorFunction err) +{ +/************************************** + * + * d e c o m p o s e + * + ************************************** + * + * Functional description + * Decompose a numeric string in mantissa and exponent, + * or if it is in hexadecimal notation. + * + **************************************/ + + RetValue value(val); + return cvt_decompose(str, len, &value, err); +} + + +class SINT64Traits +{ +public: + typedef SINT64 ValueType; + static const SINT64 UPPER_LIMIT = MAX_SINT64; + static const SINT64 LOWER_LIMIT = MIN_SINT64; +}; + +SSHORT CVT_decompose(const char* str, USHORT len, SINT64* val, ErrorFunction err) +{ +/************************************** + * + * d e c o m p o s e + * + ************************************** + * + * Functional description + * Decompose a numeric string in mantissa and exponent, + * or if it is in hexadecimal notation. + * + **************************************/ + + RetValue value(val); + return cvt_decompose(str, len, &value, err); +} + + +class I128Traits +{ +public: + typedef Int128 ValueType; + static const CInt128 UPPER_LIMIT; + static const CInt128 LOWER_LIMIT; +}; + +const CInt128 I128Traits::UPPER_LIMIT(MAX_Int128); +const CInt128 I128Traits::LOWER_LIMIT(MIN_Int128); + +SSHORT CVT_decompose(const char* str, USHORT len, Int128* val, ErrorFunction err) +{ +/************************************** + * + * d e c o m p o s e + * + ************************************** + * + * Functional description + * Decompose a numeric string in mantissa and exponent, + * or if it is in hexadecimal notation. + * + **************************************/ + + + RetValue value(val); + return cvt_decompose(str, len, &value, err); +} + + USHORT CVT_get_string_ptr_common(const dsc* desc, USHORT* ttype, UCHAR** address, vary* temp, USHORT length, DecimalStatus decSt, Callbacks* cb) { @@ -2851,10 +3031,10 @@ Decimal64 CVT_get_dec64(const dsc* desc, DecimalStatus decSt, ErrorFunction err) return *(Decimal64*) p; case dtype_dec128: - return ((Decimal128Base*) p)->toDecimal64(decSt); + return ((Decimal128*) p)->toDecimal64(decSt); - case dtype_dec_fixed: - return d64.set(*((DecimalFixed*) p), decSt, scale); + case dtype_int128: + return d64.set(*((Int128*) p), decSt, scale); default: fb_assert(false); @@ -2940,8 +3120,8 @@ Decimal128 CVT_get_dec128(const dsc* desc, DecimalStatus decSt, ErrorFunction er case dtype_dec128: return *(Decimal128*) p; - case dtype_dec_fixed: - return d128.set(*((DecimalFixed*) p), decSt, scale); + case dtype_int128: + return d128.set(*((Int128*) p), decSt, scale); default: fb_assert(false); @@ -2961,7 +3141,7 @@ Decimal128 CVT_get_dec128(const dsc* desc, DecimalStatus decSt, ErrorFunction er } -DecimalFixed CVT_get_dec_fixed(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err) +Int128 CVT_get_int128(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err) { /************************************** * @@ -2974,8 +3154,15 @@ DecimalFixed CVT_get_dec_fixed(const dsc* desc, SSHORT scale, DecimalStatus decS * **************************************/ VaryStr<1024> buffer; // represents unreasonably long decfloat literal in ASCII - DecimalFixed dfix; + Int128 int128; Decimal128 tmp; + double d, eps; + + static const double I128_MIN_dbl = -1.701411834604692e+38; + static const double I128_MAX_dbl = 1.701411834604692e+38; + static const CDecimal128 I128_MIN_dcft("-1.701411834604692317316873037158841E+38", decSt); + static const CDecimal128 I128_MAX_dcft("1.701411834604692317316873037158841E+38", decSt); + static const CDecimal128 DecFlt_05("0.5", decSt); // adjust exact numeric values to same scaling if (DTYPE_IS_EXACT(desc->dsc_dtype)) @@ -2988,27 +3175,30 @@ DecimalFixed CVT_get_dec_fixed(const dsc* desc, SSHORT scale, DecimalStatus decS switch (desc->dsc_dtype) { case dtype_short: - dfix.set(*(SSHORT*) p); + int128.set(SLONG(*(SSHORT*) p), scale); break; case dtype_long: - dfix.set(*(SLONG*) p); + int128.set(*(SLONG*) p, scale); break; case dtype_quad: - dfix.set(CVT_get_int64(desc, 0, decSt, err)); + int128.set(CVT_get_int64(desc, 0, decSt, err), scale); break; case dtype_int64: - dfix.set(*(SINT64*) p); + int128.set(*(SINT64*) p, scale); break; case dtype_varying: case dtype_cstring: case dtype_text: - CVT_make_null_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer) - 1, decSt, err); - dfix.set(buffer.vary_string, scale, decSt); - return dfix; // scale already corrected + { + USHORT length = + CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err); + scale -= CVT_decompose(p, length, &int128, err); + int128.setScale(scale); + } break; case dtype_blob: @@ -3022,23 +3212,70 @@ DecimalFixed CVT_get_dec_fixed(const dsc* desc, SSHORT scale, DecimalStatus decS break; case dtype_real: - dfix.set(*((float*) p), scale, decSt); - return dfix; // scale already corrected - case dtype_double: - dfix.set(*((double*) p), scale, decSt); - return dfix; // scale already corrected + if (desc->dsc_dtype == dtype_real) + { + d = *((float*) p); + eps = eps_float; + } + else // if (desc->dsc_dtype == DEFAULT_DOUBLE) + { + d = *((double*) p); + eps = eps_double; + } + + if (scale > 0) + d /= CVT_power_of_ten(scale); + else if (scale < 0) + d *= CVT_power_of_ten(-scale); + + if (d > 0) + d += 0.5 + eps; + else + d -= 0.5 + eps; + + /* make sure the cast will succeed + + Note that adding or subtracting 0.5, as we do in CVT_get_long, + will never allow the rounded value to fit into an Int128, + because when the double value is too large in magnitude + to fit, 0.5 is less than half of the least significant bit + of the significant (sometimes miscalled "mantissa") of the + double, and thus will have no effect on the sum. */ + + if (d < I128_MIN_dbl || I128_MAX_dbl < d) + err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + + int128.set(d); + break; case dtype_dec64: - dfix = tmp = *((Decimal64*) p); - break; - case dtype_dec128: - dfix = *((Decimal128*) p); + if (desc->dsc_dtype == dtype_dec64) + tmp = *((Decimal64*) p); + else + tmp = *((Decimal128*) p); + + tmp.setScale(decSt, -scale); + + /* make sure the cast will succeed + + Note that adding or subtracting 0.5, as we do in CVT_get_long, + will never allow the rounded value to fit into an Int128, + because when the double value is too large in magnitude + to fit, 0.5 is less than half of the least significant bit + of the significant (sometimes miscalled "mantissa") of the + double, and thus will have no effect on the sum. */ + + if (tmp.compare(decSt, I128_MIN_dcft) < 0 || I128_MAX_dcft.compare(decSt, tmp) < 0) + err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + + int128.set(decSt, tmp); break; - case dtype_dec_fixed: - dfix = *((DecimalFixed*) p); + case dtype_int128: + int128 = *((Int128*) p); + int128.setScale(scale); break; default: @@ -3054,8 +3291,7 @@ DecimalFixed CVT_get_dec_fixed(const dsc* desc, SSHORT scale, DecimalStatus decS err(v); } - dfix.exactInt(decSt, scale); - return dfix; + return int128; } @@ -3154,7 +3390,9 @@ SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunc { USHORT length = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err); - scale -= CVT_decompose(p, length, dtype_quad, &value.gds_quad_high, err); + SINT64 i64; + scale -= CVT_decompose(p, length, &i64, err); + SINT64_to_SQUAD(i64, value); } break; @@ -3170,7 +3408,7 @@ SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunc case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: SINT64_to_SQUAD(CVT_get_int64(desc, scale, decSt, err), value); break; @@ -3246,9 +3484,8 @@ SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFu return d128.toInt64(decSt, scale); } - case dtype_dec_fixed: - value = ((DecimalFixed*) p)->toInt64(decSt); - break; + case dtype_int128: + return ((Int128*) p)->toInt64(scale); case dtype_real: case dtype_double: @@ -3295,7 +3532,7 @@ SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFu { USHORT length = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err); - scale -= CVT_decompose(p, length, dtype_int64, (SLONG *) & value, err); + scale -= CVT_decompose(p, length, &value, err); } break; @@ -3349,7 +3586,7 @@ SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFu } -static SINT64 hex_to_value(const char*& string, const char* end) +static void hex_to_value(const char*& string, const char* end, RetPtr* retValue) /************************************* * * hex_to_value @@ -3366,7 +3603,6 @@ static SINT64 hex_to_value(const char*& string, const char* end) // we already know this is a hex string, and there is no prefix. // So, string is something like DEADBEEF. - SINT64 value = 0; UCHAR byte = 0; int nibble = ((end - string) & 1); char ch; @@ -3385,7 +3621,7 @@ static SINT64 hex_to_value(const char*& string, const char* end) { byte = (byte << 4) + (UCHAR) c; nibble = 0; - value = (value << 8) + byte; + retValue->nextDigit(byte, 256); } else { @@ -3397,8 +3633,6 @@ static SINT64 hex_to_value(const char*& string, const char* end) } fb_assert(string <= end); - - return value; } diff --git a/src/common/cvt.h b/src/common/cvt.h index b7e5dd31f7..5359fb6182 100644 --- a/src/common/cvt.h +++ b/src/common/cvt.h @@ -80,7 +80,9 @@ enum EXPECT_DATETIME expect_sql_time_tz }; -} +class Int128; + +} // namespace Firebird void CVT_conversion_error(const dsc*, ErrorFunction); @@ -90,12 +92,14 @@ bool CVT_get_boolean(const dsc*, ErrorFunction); double CVT_get_double(const dsc*, Firebird::DecimalStatus, ErrorFunction, bool* getNumericOverflow = nullptr); Firebird::Decimal64 CVT_get_dec64(const dsc*, Firebird::DecimalStatus, ErrorFunction); Firebird::Decimal128 CVT_get_dec128(const dsc*, Firebird::DecimalStatus, ErrorFunction); -Firebird::DecimalFixed CVT_get_dec_fixed(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); +Firebird::Int128 CVT_get_int128(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); USHORT CVT_make_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); void CVT_make_null_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); void CVT_move_common(const dsc*, dsc*, Firebird::DecimalStatus, Firebird::Callbacks*); void CVT_move(const dsc*, dsc*, Firebird::DecimalStatus, ErrorFunction); -SSHORT CVT_decompose(const char*, USHORT, SSHORT, SLONG*, ErrorFunction); +SSHORT CVT_decompose(const char*, USHORT, SLONG*, ErrorFunction); +SSHORT CVT_decompose(const char*, USHORT, SINT64*, ErrorFunction); +SSHORT CVT_decompose(const char*, USHORT, Firebird::Int128*, ErrorFunction); USHORT CVT_get_string_ptr(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); USHORT CVT_get_string_ptr_common(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, Firebird::Callbacks*); SINT64 CVT_get_int64(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); diff --git a/src/common/dsc.cpp b/src/common/dsc.cpp index 75810a1729..6601a26b36 100644 --- a/src/common/dsc.cpp +++ b/src/common/dsc.cpp @@ -68,7 +68,7 @@ static const USHORT _DSC_convert_to_text_length[DTYPE_TYPE_MAX] = 5, // dtype_boolean 23, // dtype_dec64 1 + 1 + 1 + 1 + 16(34) + 3(4) 42, // dtype_dec128 +- . e +- coeff + exp - 36, // dtype_dec_fixed coeff(34) + 1(+-) + 1(.) + 47, // dtype_int128 14 + TimeZoneUtil::MAX_LEN, // dtype_sql_time_tz HH:MM:SS.MMMM +NN:NN 25 + TimeZoneUtil::MAX_LEN // dtype_timestamp_tz YYYY-MM-DD HH:MM:SS.MMMM +NN:NN }; @@ -122,7 +122,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double dtype_d_float dtype_sql_date dtype_sql_time dtype_timestamp dtype_blob dtype_array dtype_int64 dtype_dbkey dtype_boolean dtype_dec64 dtype_dec128 - dtype_dec_fixed dtype_sql_time_tz dtype_timestamp_tz + dtype_int128 dtype_sql_time_tz dtype_timestamp_tz */ // dtype_unknown @@ -141,7 +141,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, dtype_timestamp_tz}, + dtype_int128, DTYPE_CANNOT, dtype_timestamp_tz}, // dtype_cstring {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -150,7 +150,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, dtype_timestamp_tz}, + dtype_int128, DTYPE_CANNOT, dtype_timestamp_tz}, // dtype_varying {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -159,7 +159,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, dtype_timestamp_tz}, + dtype_int128, DTYPE_CANNOT, dtype_timestamp_tz}, // 4 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -204,7 +204,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, dtype_sql_time_tz, dtype_timestamp_tz}, + dtype_int128, dtype_sql_time_tz, dtype_timestamp_tz}, // dtype_long {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -213,7 +213,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, dtype_sql_time_tz, dtype_timestamp_tz}, + dtype_int128, dtype_sql_time_tz, dtype_timestamp_tz}, // dtype_quad {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -303,7 +303,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, dtype_sql_time_tz, dtype_timestamp_tz}, + dtype_int128, dtype_sql_time_tz, dtype_timestamp_tz}, // dtype_dbkey {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -341,14 +341,14 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, dtype_dec128, dtype_sql_time_tz, dtype_timestamp_tz}, - // dtype_dec_fixed - {dtype_unknown, dtype_dec_fixed, dtype_dec_fixed, dtype_dec_fixed, + // dtype_int128 + {dtype_unknown, dtype_int128, dtype_int128, dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - dtype_dec_fixed, dtype_dec_fixed, DTYPE_CANNOT, dtype_double, + dtype_int128, dtype_int128, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, - dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec_fixed, + dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, dtype_sql_time_tz, dtype_timestamp_tz}, + dtype_int128, dtype_sql_time_tz, dtype_timestamp_tz}, // dtype_sql_time_tz {dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -384,7 +384,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double dtype_d_float dtype_sql_date dtype_sql_time dtype_timestamp dtype_blob dtype_array dtype_int64 dtype_dbkey dtype_boolean dtype_dec64 dtype_dec128 - dtype_dec_fixed dtype_sql_time_tz dtype_timestamp_tz + dtype_int128 dtype_sql_time_tz dtype_timestamp_tz */ // dtype_unknown @@ -403,7 +403,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, dtype_timestamp_tz}, + dtype_int128, DTYPE_CANNOT, dtype_timestamp_tz}, // dtype_cstring {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -412,7 +412,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, dtype_timestamp_tz}, + dtype_int128, DTYPE_CANNOT, dtype_timestamp_tz}, // dtype_varying {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -421,7 +421,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, dtype_timestamp_tz}, + dtype_int128, DTYPE_CANNOT, dtype_timestamp_tz}, // 4 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -466,7 +466,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_long {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -475,7 +475,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_quad {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -565,7 +565,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_dbkey {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -603,14 +603,14 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, dtype_dec128, DTYPE_CANNOT, DTYPE_CANNOT}, - // dtype_dec_fixed - {dtype_unknown, dtype_dec_fixed, dtype_dec_fixed, dtype_dec_fixed, + // dtype_int128 + {dtype_unknown, dtype_int128, dtype_int128, dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - dtype_dec_fixed, dtype_dec_fixed, DTYPE_CANNOT, dtype_double, + dtype_int128, dtype_int128, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec_fixed, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_sql_time_tz {dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -647,7 +647,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double dtype_d_float dtype_sql_date dtype_sql_time dtype_timestamp dtype_blob dtype_array dtype_int64 dtype_dbkey dtype_boolean dtype_dec64 dtype_dec128 - dtype_dec_fixed dtype_sql_time_tz dtype_timestamp_tz + dtype_int128 dtype_sql_time_tz dtype_timestamp_tz */ // dtype_unknown @@ -666,7 +666,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_cstring {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -675,7 +675,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_varying {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -684,7 +684,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // 4 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -729,7 +729,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_long {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -738,7 +738,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_quad {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -826,9 +826,9 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_dbkey {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -866,14 +866,14 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, dtype_dec128, DTYPE_CANNOT, DTYPE_CANNOT}, - // dtype_dec_fixed - {dtype_unknown, dtype_dec_fixed, dtype_dec_fixed, dtype_dec_fixed, + // dtype_int128 + {dtype_unknown, dtype_int128, dtype_int128, dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - dtype_dec_fixed, dtype_dec_fixed, DTYPE_CANNOT, dtype_double, + dtype_int128, dtype_int128, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec_fixed, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_sql_time_tz {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -909,7 +909,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double dtype_d_float dtype_sql_date dtype_sql_time dtype_timestamp dtype_blob dtype_array dtype_int64 dtype_dbkey dtype_boolean dtype_dec64 dtype_dec128 - dtype_dec_fixed dtype_sql_time_tz dtype_timestamp_tz + dtype_int128 dtype_sql_time_tz dtype_timestamp_tz */ // dtype_unknown @@ -928,7 +928,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_cstring {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -937,7 +937,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_varying {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -946,7 +946,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // 4 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -991,7 +991,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_long {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -1000,7 +1000,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_quad {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -1090,7 +1090,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_dbkey {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -1128,14 +1128,14 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, dtype_dec128, DTYPE_CANNOT, DTYPE_CANNOT}, - // dtype_dec_fixed - {dtype_unknown, dtype_dec_fixed, dtype_dec_fixed, dtype_dec_fixed, + // dtype_int128 + {dtype_unknown, dtype_int128, dtype_int128, dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - dtype_dec_fixed, dtype_dec_fixed, DTYPE_CANNOT, dtype_double, + dtype_int128, dtype_int128, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec_fixed, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128, - dtype_dec_fixed, DTYPE_CANNOT, DTYPE_CANNOT}, + dtype_int128, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_sql_time_tz {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -1268,9 +1268,9 @@ bool DSC_make_descriptor(DSC* desc, desc->dsc_dtype = dtype_dec128; break; - case blr_dec_fixed: - desc->dsc_length = sizeof(DecimalFixed); - desc->dsc_dtype = dtype_dec_fixed; + case blr_int128: + desc->dsc_length = sizeof(Int128); + desc->dsc_dtype = dtype_int128; break; case blr_timestamp: @@ -1448,7 +1448,7 @@ const char* dsc::typeToText() const return "decfloat(16)"; case dtype_dec128: return "decfloat(34)"; - case dtype_dec_fixed: + case dtype_int128: return "decimal"; case dtype_sql_time_tz: return "time with timezone"; @@ -1552,8 +1552,8 @@ void dsc::getSqlInfo(SLONG* sqlLength, SLONG* sqlSubType, SLONG* sqlScale, SLONG *sqlScale = 0; break; - case dtype_dec_fixed: - *sqlType = SQL_DEC_FIXED; + case dtype_int128: + *sqlType = SQL_INT128; *sqlScale = dsc_scale; if (dsc_sub_type) *sqlSubType = dsc_sub_type; diff --git a/src/common/dsc.h b/src/common/dsc.h index b7b78d078b..228ffbda6c 100644 --- a/src/common/dsc.h +++ b/src/common/dsc.h @@ -32,6 +32,7 @@ #include "../jrd/ods.h" #include "../intl/charsets.h" #include "../common/DecFloat.h" +#include "../common/Int128.h" // Data type information @@ -60,7 +61,7 @@ inline bool DTYPE_IS_BLOB_OR_QUAD(UCHAR d) // Exact numeric? inline bool DTYPE_IS_EXACT(UCHAR d) { - return d == dtype_int64 || d == dtype_long || d == dtype_short || d == dtype_dec_fixed; + return d == dtype_int64 || d == dtype_long || d == dtype_short || d == dtype_int128; } inline bool DTYPE_IS_APPROX(UCHAR d) @@ -70,12 +71,13 @@ inline bool DTYPE_IS_APPROX(UCHAR d) inline bool DTYPE_IS_DECFLOAT(UCHAR d) { - return d == dtype_dec128 || d == dtype_dec64 || d == dtype_dec_fixed; + return d == dtype_dec128 || d == dtype_dec64; } inline bool DTYPE_IS_NUMERIC(UCHAR d) { - return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64 || DTYPE_IS_DECFLOAT(d); + return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64 || + d == dtype_int128 || DTYPE_IS_DECFLOAT(d); } // Descriptor format @@ -138,7 +140,8 @@ typedef struct dsc bool isExact() const { - return dsc_dtype == dtype_int64 || dsc_dtype == dtype_long || dsc_dtype == dtype_short; + return dsc_dtype == dtype_int128 || dsc_dtype == dtype_int64 || + dsc_dtype == dtype_long || dsc_dtype == dtype_short; } bool isNumeric() const @@ -187,14 +190,14 @@ typedef struct dsc return dsc_dtype == dtype_dec128 || dsc_dtype == dtype_dec64; } - bool isDecFixed() const + bool isInt128() const { - return dsc_dtype == dtype_dec_fixed; + return dsc_dtype == dtype_int128; } bool isDecOrInt() const { - return isDecFloat() || isDecFixed() || isExact(); + return isDecFloat() || isExact(); } bool isApprox() const @@ -339,6 +342,15 @@ typedef struct dsc dsc_address = (UCHAR*) address; } + void makeInt128(SCHAR scale, Firebird::Int128* address = NULL) + { + clear(); + dsc_dtype = dtype_int128; + dsc_length = sizeof(Firebird::Int128); + dsc_scale = scale; + dsc_address = (UCHAR*) address; + } + void makeLong(SCHAR scale, SLONG* address = NULL) { clear(); diff --git a/src/common/keywords.cpp b/src/common/keywords.cpp index 6d667d8015..17a4b70718 100644 --- a/src/common/keywords.cpp +++ b/src/common/keywords.cpp @@ -252,6 +252,7 @@ static const TOK tokens[] = {TOK_INSERT, "INSERT", false}, {TOK_INSERTING, "INSERTING", false}, {TOK_INT, "INT", false}, + {TOK_INT128, "INT128", true}, {TOK_INTEGER, "INTEGER", false}, {TOK_INTO, "INTO", false}, {TOK_INVOKER, "INVOKER", true}, diff --git a/src/common/sdl.cpp b/src/common/sdl.cpp index 3080f4dbd7..af220e55b2 100644 --- a/src/common/sdl.cpp +++ b/src/common/sdl.cpp @@ -855,9 +855,9 @@ static const UCHAR* sdl_desc(const UCHAR* ptr, DSC* desc) desc->dsc_length = sizeof(Decimal128); break; - case blr_dec_fixed: - desc->dsc_dtype = dtype_dec_fixed; - desc->dsc_length = sizeof(DecimalFixed); + case blr_int128: + desc->dsc_dtype = dtype_int128; + desc->dsc_length = sizeof(Int128); break; case blr_timestamp: diff --git a/src/common/utils.cpp b/src/common/utils.cpp index f4ecba2260..4a236f6dc1 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -1522,8 +1522,8 @@ UCHAR sqlTypeToDscType(SSHORT sqlType) return dtype_dec64; case SQL_DEC34: return dtype_dec128; - case SQL_DEC_FIXED: - return dtype_dec_fixed; + case SQL_INT128: + return dtype_int128; case SQL_TIME_TZ: return dtype_sql_time_tz; case SQL_TIMESTAMP_TZ: diff --git a/src/common/xdr.cpp b/src/common/xdr.cpp index 4c4c64d8ec..ac53a6e46c 100644 --- a/src/common/xdr.cpp +++ b/src/common/xdr.cpp @@ -31,6 +31,7 @@ #include "../yvalve/gds_proto.h" #include "../common/gdsassert.h" #include "../common/DecFloat.h" +#include "../common/Int128.h" inline UCHAR* XDR_ALLOC(ULONG size) { @@ -268,12 +269,17 @@ bool_t xdr_datum( XDR* xdrs, const dsc* desc, UCHAR* buffer) break; case dtype_dec128: - case dtype_dec_fixed: fb_assert(desc->dsc_length >= sizeof(Firebird::Decimal128)); if (!xdr_dec128(xdrs, reinterpret_cast(p))) return FALSE; break; + case dtype_int128: + fb_assert(desc->dsc_length >= sizeof(Firebird::Int128)); + if (!xdr_int128(xdrs, reinterpret_cast(p))) + return FALSE; + break; + case dtype_timestamp: fb_assert(desc->dsc_length >= 2 * sizeof(SLONG)); if (!xdr_long(xdrs, &((SLONG*) p)[0])) @@ -381,6 +387,19 @@ bool_t xdr_dec128(XDR* xdrs, Firebird::Decimal128* ip) } +bool_t xdr_int128(XDR* xdrs, Firebird::Int128* ip) +{ + UCHAR* bytes = ip->getBytes(); + +#ifndef WORDS_BIGENDIAN + return xdr_hyper(xdrs, &bytes[8]) && xdr_hyper(xdrs, &bytes[0]); +#else + fb_assert(false); // Dec64/128 XDR not tested on bigendians! + return xdr_hyper(xdrs, &bytes[0]) && xdr_hyper(xdrs, &bytes[8]); +#endif +} + + bool_t xdr_enum(XDR* xdrs, xdr_op* ip) { /************************************** diff --git a/src/common/xdr_proto.h b/src/common/xdr_proto.h index b9387e5363..136f81411d 100644 --- a/src/common/xdr_proto.h +++ b/src/common/xdr_proto.h @@ -29,8 +29,9 @@ bool_t xdr_datum(XDR*, const dsc*, UCHAR*); bool_t xdr_double(XDR*, double*); -bool_t xdr_dec64(XDR*, Firebird::Decimal64*); -bool_t xdr_dec128(XDR*, Firebird::Decimal128*); +bool_t xdr_dec64(XDR*, Firebird::Decimal64*); +bool_t xdr_dec128(XDR*, Firebird::Decimal128*); +bool_t xdr_int128(XDR*, Firebird::Int128*); bool_t xdr_enum(XDR*, xdr_op*); bool_t xdr_float(XDR*, float*); bool_t xdr_int(XDR*, int*); diff --git a/src/dsql/AggNodes.cpp b/src/dsql/AggNodes.cpp index 067d31193f..94fec8a07a 100644 --- a/src/dsql/AggNodes.cpp +++ b/src/dsql/AggNodes.cpp @@ -540,6 +540,11 @@ void AvgAggNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) ERRD_post(Arg::Gds(isc_expression_eval_err) << Arg::Gds(isc_dsql_agg2_wrongarg) << Arg::Str("AVG")); } + else if (desc->dsc_dtype == dtype_int64 || desc->dsc_dtype == dtype_int128) + { + desc->dsc_dtype = dtype_int128; + desc->dsc_length = sizeof(Int128); + } else if (DTYPE_IS_EXACT(desc->dsc_dtype)) { desc->dsc_dtype = dtype_int64; @@ -584,13 +589,10 @@ void AvgAggNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) return; } - // In V6, the average of an exact type is computed in SINT64, rather than double as in prior - // releases. switch (desc->dsc_dtype) { case dtype_short: case dtype_long: - case dtype_int64: desc->dsc_dtype = dtype_int64; desc->dsc_length = sizeof(SINT64); desc->dsc_sub_type = 0; @@ -598,6 +600,15 @@ void AvgAggNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) nodScale = desc->dsc_scale; break; + case dtype_int64: + case dtype_int128: + desc->dsc_dtype = dtype_int128; + desc->dsc_length = sizeof(Int128); + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + nodScale = desc->dsc_scale; + break; + case dtype_unknown: desc->dsc_dtype = dtype_unknown; desc->dsc_length = 0; @@ -697,12 +708,19 @@ dsc* AvgAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const SINT64 i; double d; Decimal128 dec; + Int128 i128; if (!dialect1 && impure->vlu_desc.dsc_dtype == dtype_int64) { i = *((SINT64*) impure->vlu_desc.dsc_address) / impure->vlux_count; temp.makeInt64(impure->vlu_desc.dsc_scale, &i); } + else if (!dialect1 && impure->vlu_desc.dsc_dtype == dtype_int128) + { + i128.set(impure->vlux_count, 0); + i128 = ((Int128*) impure->vlu_desc.dsc_address)->div(i128, 0); + temp.makeInt128(impure->vlu_desc.dsc_scale, &i128); + } else if (DTYPE_IS_DECFLOAT(impure->vlu_desc.dsc_dtype)) { DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; @@ -1037,6 +1055,11 @@ void SumAggNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) ERRD_post(Arg::Gds(isc_expression_eval_err) << Arg::Gds(isc_dsql_agg2_wrongarg) << Arg::Str("SUM")); } + else if (desc->dsc_dtype == dtype_int64 || desc->dsc_dtype == dtype_int128) + { + desc->dsc_dtype = dtype_int128; + desc->dsc_length = sizeof(Int128); + } else if (DTYPE_IS_EXACT(desc->dsc_dtype)) { desc->dsc_dtype = dtype_int64; @@ -1118,13 +1141,21 @@ void SumAggNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { case dtype_short: case dtype_long: - case dtype_int64: desc->dsc_dtype = dtype_int64; desc->dsc_length = sizeof(SINT64); nodScale = desc->dsc_scale; desc->dsc_flags = 0; return; + case dtype_int64: + case dtype_int128: + desc->dsc_dtype = dtype_int128; + desc->dsc_length = sizeof(Int128); + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + nodScale = desc->dsc_scale; + return; + case dtype_unknown: desc->dsc_dtype = dtype_unknown; desc->dsc_length = 0; diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 59bb0a6c98..31e45ae4a1 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -4491,7 +4491,7 @@ void AlterDomainNode::checkUpdate(const dyn_fld& origFld, const dyn_fld& newFld) case blr_float: case blr_dec64: case blr_dec128: - case blr_dec_fixed: + case blr_int128: // Cannot convert column %s from character to non-character data. errorCode = isc_dyn_dtype_conv_invalid; break; @@ -4613,7 +4613,7 @@ void AlterDomainNode::checkUpdate(const dyn_fld& origFld, const dyn_fld& newFld) case blr_float: case blr_dec64: case blr_dec128: - case blr_dec_fixed: + case blr_int128: switch (newFld.dyn_dtype) { case blr_blob: @@ -4741,7 +4741,7 @@ void AlterDomainNode::checkUpdate(const dyn_fld& origFld, const dyn_fld& newFld) case blr_double: case blr_dec64: case blr_dec128: - case blr_dec_fixed: + case blr_int128: break; default: @@ -4751,13 +4751,13 @@ void AlterDomainNode::checkUpdate(const dyn_fld& origFld, const dyn_fld& newFld) } break; - case blr_dec_fixed: + case blr_int128: switch (origFld.dyn_dtype) { case blr_short: case blr_long: case blr_int64: - case blr_dec_fixed: + case blr_int128: break; default: diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index dd75a5d6b7..b7d2263625 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -506,17 +506,19 @@ namespace const UCHAR DSC_ZTYPE_FLT64 = 0; const UCHAR DSC_ZTYPE_FLT128 = 1; const UCHAR DSC_ZTYPE_FIXED = 2; -const UCHAR DSC_ZTYPE_INT = 3; -const UCHAR DSC_ZTYPE_OTHER = 4; -const UCHAR DSC_ZTYPE_BAD = 5; +const UCHAR DSC_ZTYPE_INT64 = 3; +const UCHAR DSC_ZTYPE_INT = 4; +const UCHAR DSC_ZTYPE_OTHER = 5; +const UCHAR DSC_ZTYPE_BAD = 6; -const UCHAR decimalDescTable[5][5] = { -/* DSC_ZTYPE_FLT64 DSC_ZTYPE_FLT128 DSC_ZTYPE_FIXED DSC_ZTYPE_INT DSC_ZTYPE_OTHER */ -/* DSC_ZTYPE_FLT64 */ {DSC_ZTYPE_FLT64, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128}, -/* DSC_ZTYPE_FLT128 */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128}, -/* DSC_ZTYPE_FIXED */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FIXED, DSC_ZTYPE_FIXED, DSC_ZTYPE_FLT128}, -/* DSC_ZTYPE_INT */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FIXED, DSC_ZTYPE_BAD, DSC_ZTYPE_BAD}, -/* DSC_ZTYPE_OTHER */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_BAD, DSC_ZTYPE_BAD} +const UCHAR decimalDescTable[6][6] = { +/* DSC_ZTYPE_FLT64 DSC_ZTYPE_FLT128 DSC_ZTYPE_FIXED DSC_ZTYPE_INT64 DSC_ZTYPE_INT DSC_ZTYPE_OTHER */ +/* DSC_ZTYPE_FLT64 */ {DSC_ZTYPE_FLT64, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128}, +/* DSC_ZTYPE_FLT128 */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128}, +/* DSC_ZTYPE_FIXED */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FIXED, DSC_ZTYPE_FIXED, DSC_ZTYPE_FIXED, DSC_ZTYPE_FLT128}, +/* DSC_ZTYPE_INT64 */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FIXED, DSC_ZTYPE_FIXED, DSC_ZTYPE_BAD, DSC_ZTYPE_BAD}, +/* DSC_ZTYPE_INT */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FIXED, DSC_ZTYPE_BAD, DSC_ZTYPE_BAD, DSC_ZTYPE_BAD}, +/* DSC_ZTYPE_OTHER */ {DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_FLT128, DSC_ZTYPE_BAD, DSC_ZTYPE_BAD, DSC_ZTYPE_BAD} }; UCHAR getFType(const dsc& desc) @@ -527,8 +529,10 @@ UCHAR getFType(const dsc& desc) return DSC_ZTYPE_FLT64; case dtype_dec128: return DSC_ZTYPE_FLT128; - case dtype_dec_fixed: + case dtype_int128: return DSC_ZTYPE_FIXED; + case dtype_int64: + return DSC_ZTYPE_INT64; } if (DTYPE_IS_EXACT(desc.dsc_dtype)) @@ -547,7 +551,7 @@ unsigned setDecDesc(dsc* desc, const dsc& desc1, const dsc& desc2, Scaling sc, S zipType = DSC_ZTYPE_FLT128; // In production case fallback to Decimal128 desc->dsc_dtype = zipType == DSC_ZTYPE_FLT64 ? dtype_dec64 : - zipType == DSC_ZTYPE_FLT128 ? dtype_dec128 : dtype_dec_fixed; + zipType == DSC_ZTYPE_FLT128 ? dtype_dec128 : dtype_int128; desc->dsc_sub_type = 0; desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; desc->dsc_scale = 0; @@ -569,9 +573,9 @@ unsigned setDecDesc(dsc* desc, const dsc& desc1, const dsc& desc2, Scaling sc, S *nodScale = desc->dsc_scale; desc->dsc_length = zipType == DSC_ZTYPE_FLT64 ? sizeof(Decimal64) : - zipType == DSC_ZTYPE_FLT128 ? sizeof(Decimal128) : sizeof(DecimalFixed); + zipType == DSC_ZTYPE_FLT128 ? sizeof(Decimal128) : sizeof(Int128); - return zipType == DSC_ZTYPE_FIXED ? ExprNode::FLAG_DECFIXED : ExprNode::FLAG_DECFLOAT; + return zipType == DSC_ZTYPE_FIXED ? ExprNode::FLAG_INT128 : ExprNode::FLAG_DECFLOAT; } } // anon namespace @@ -764,7 +768,7 @@ void ArithmeticNode::makeDialect1(dsc* desc, dsc& desc1, dsc& desc2) case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: setDecDesc(desc, desc1, desc2, SCALE_MIN); break; @@ -792,7 +796,7 @@ void ArithmeticNode::makeDialect1(dsc* desc, dsc& desc1, dsc& desc2) switch (dtype) { case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: setDecDesc(desc, desc1, desc2, SCALE_SUM); break; @@ -887,8 +891,8 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) // arithmetic, but returns a if (DTYPE_IS_EXACT(dtype1) && DTYPE_IS_EXACT(dtype2)) { - if (desc1.isDecFixed() || desc2.isDecFixed()) - dtype = dtype_dec_fixed; + if (desc1.isInt128() || desc2.isInt128()) + dtype = dtype_int128; else dtype = dtype_int64; } @@ -1027,7 +1031,7 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: setDecDesc(desc, desc1, desc2, SCALE_MIN); break; @@ -1076,7 +1080,7 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) switch (dtype) { - case dtype_dec_fixed: + case dtype_int128: case dtype_dec128: setDecDesc(desc, desc1, desc2, SCALE_SUM); break; @@ -1135,7 +1139,7 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) break; case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: setDecDesc(desc, desc1, desc2, SCALE_SUM); break; @@ -1347,7 +1351,7 @@ void ArithmeticNode::getDescDialect1(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: nodFlags |= setDecDesc(desc, desc1, desc2, SCALE_MIN, &nodScale); break; @@ -1394,7 +1398,7 @@ void ArithmeticNode::getDescDialect1(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, return; case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: nodFlags |= setDecDesc(desc, desc1, desc2, SCALE_SUM, &nodScale); break; @@ -1469,8 +1473,8 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, if (DTYPE_IS_EXACT(dtype1) && DTYPE_IS_EXACT(dtype2)) { - if (desc1.isDecFixed() || desc2.isDecFixed()) - dtype = dtype_dec_fixed; + if (desc1.isInt128() || desc2.isInt128()) + dtype = dtype_int128; else dtype = dtype_int64; } @@ -1620,7 +1624,7 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: nodFlags |= setDecDesc(desc, desc1, desc2, SCALE_MIN, &nodScale); return; @@ -1674,7 +1678,7 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, return; case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: nodFlags |= setDecDesc(desc, desc1, desc2, SCALE_SUM, &nodScale); return; @@ -1900,23 +1904,6 @@ dsc* ArithmeticNode::add(thread_db* tdbb, const dsc* desc, impure_value* value, return result; } - if (node->nodFlags & FLAG_DECFIXED) - { - const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, desc, node->nodScale); - const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, node->nodScale); - - DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; - value->vlu_misc.vlu_dec_fixed = (blrOp == blr_subtract) ? d2.sub(decSt, d1) : d1.add(decSt, d2); - - result->dsc_dtype = dtype_dec_fixed; - result->dsc_length = sizeof(DecimalFixed); - result->dsc_scale = node->nodScale; - result->dsc_sub_type = 0; - result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed; - - return result; - } - // Handle floating arithmetic if (node->nodFlags & FLAG_DOUBLE) @@ -1996,19 +1983,20 @@ dsc* ArithmeticNode::add2(thread_db* tdbb, const dsc* desc, impure_value* value, return result; } - if (node->nodFlags & FLAG_DECFIXED) + // 128-bit arithmetic + + if (node->nodFlags & FLAG_INT128) { - const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, desc, node->nodScale); - const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, node->nodScale); + const Int128 d1 = MOV_get_int128(tdbb, desc, node->nodScale); + const Int128 d2 = MOV_get_int128(tdbb, &value->vlu_desc, node->nodScale); - DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; - value->vlu_misc.vlu_dec_fixed = (blrOp == blr_subtract) ? d2.sub(decSt, d1) : d1.add(decSt, d2); + value->vlu_misc.vlu_int128 = (blrOp == blr_subtract) ? d2.sub(d1) : d1.add(d2); - result->dsc_dtype = dtype_dec_fixed; - result->dsc_length = sizeof(DecimalFixed); + result->dsc_dtype = dtype_int128; + result->dsc_length = sizeof(Int128); result->dsc_scale = node->nodScale; result->dsc_sub_type = 0; - result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed; + result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_int128; return result; } @@ -2099,19 +2087,20 @@ dsc* ArithmeticNode::multiply(const dsc* desc, impure_value* value) const return &value->vlu_desc; } - if (nodFlags & FLAG_DECFIXED) + // 128-bit arithmetic + + if (nodFlags & FLAG_INT128) { - const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, desc, nodScale); - const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, nodScale); + const Int128 d1 = MOV_get_int128(tdbb, desc, nodScale); + const Int128 d2 = MOV_get_int128(tdbb, &value->vlu_desc, nodScale); - DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; - value->vlu_misc.vlu_dec_fixed = d1.mul(decSt, d2); + value->vlu_misc.vlu_int128 = d1.mul(d2); - value->vlu_desc.dsc_dtype = dtype_dec_fixed; - value->vlu_desc.dsc_length = sizeof(DecimalFixed); + value->vlu_desc.dsc_dtype = dtype_int128; + value->vlu_desc.dsc_length = sizeof(Int128); value->vlu_desc.dsc_scale = nodScale; value->vlu_desc.dsc_sub_type = 0; - value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed; + value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_int128; return &value->vlu_desc; } @@ -2210,20 +2199,21 @@ dsc* ArithmeticNode::multiply2(const dsc* desc, impure_value* value) const return &value->vlu_desc; } - if (nodFlags & FLAG_DECFIXED) + // 128-bit arithmetic + + if (nodFlags & FLAG_INT128) { const SSHORT scale = NUMERIC_SCALE(*desc); - const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, desc, scale); - const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, nodScale - scale); + const Int128 d1 = MOV_get_int128(tdbb, desc, scale); + const Int128 d2 = MOV_get_int128(tdbb, &value->vlu_desc, nodScale - scale); - DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; - value->vlu_misc.vlu_dec_fixed = d1.mul(decSt, d2); + value->vlu_misc.vlu_int128 = d1.mul(d2); - value->vlu_desc.dsc_dtype = dtype_dec_fixed; - value->vlu_desc.dsc_length = sizeof(DecimalFixed); + value->vlu_desc.dsc_dtype = dtype_int128; + value->vlu_desc.dsc_length = sizeof(Int128); value->vlu_desc.dsc_scale = nodScale; value->vlu_desc.dsc_sub_type = 0; - value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed; + value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_int128; return &value->vlu_desc; } @@ -2324,20 +2314,21 @@ dsc* ArithmeticNode::divide2(const dsc* desc, impure_value* value) const return &value->vlu_desc; } - if (nodFlags & FLAG_DECFIXED) + // 128-bit arithmetic + + if (nodFlags & FLAG_INT128) { const SSHORT scale = NUMERIC_SCALE(*desc); - const DecimalFixed d2 = MOV_get_dec_fixed(tdbb, desc, scale); - const DecimalFixed d1 = MOV_get_dec_fixed(tdbb, &value->vlu_desc, nodScale - scale); + const Int128 d2 = MOV_get_int128(tdbb, desc, scale); + const Int128 d1 = MOV_get_int128(tdbb, &value->vlu_desc, nodScale - scale); - DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; - value->vlu_misc.vlu_dec_fixed = d1.div(decSt, d2, scale * 2); + value->vlu_misc.vlu_int128 = d1.div(d2, scale * 2); - value->vlu_desc.dsc_dtype = dtype_dec_fixed; - value->vlu_desc.dsc_length = sizeof(DecimalFixed); + value->vlu_desc.dsc_dtype = dtype_int128; + value->vlu_desc.dsc_length = sizeof(Int128); value->vlu_desc.dsc_scale = nodScale; value->vlu_desc.dsc_sub_type = 0; - value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec_fixed; + value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_int128; return &value->vlu_desc; } @@ -7383,18 +7374,17 @@ DmlNode* LiteralNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* case dtype_double: case dtype_dec128: { - SSHORT scale; - UCHAR dtype; - // The double literal could potentially be used for any numeric literal - the value is // passed as if it were a text string. Convert the numeric string to its binary value // (int64, long or double as appropriate). l = csb->csb_blr_reader.getWord(); q = csb->csb_blr_reader.getPos(); - dtype = CVT_get_numeric(q, l, &scale, p); + SSHORT scale = 0; + UCHAR dtype = CVT_get_numeric(q, l, &scale, p); node->litDesc.dsc_dtype = dtype; node->dsqlStr = FB_NEW_POOL(pool) IntlString(pool, string(q, l)); + node->litDesc.dsc_scale = (SCHAR) scale; switch (dtype) { @@ -7406,11 +7396,9 @@ DmlNode* LiteralNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* break; case dtype_long: node->litDesc.dsc_length = sizeof(SLONG); - node->litDesc.dsc_scale = (SCHAR) scale; break; default: node->litDesc.dsc_length = sizeof(SINT64); - node->litDesc.dsc_scale = (SCHAR) scale; } break; } @@ -7846,25 +7834,34 @@ bool LiteralNode::sameAs(CompilerScratch* csb, const ExprNode* other, bool ignor ValueExprNode* LiteralNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - if (DTYPE_IS_DECFLOAT(csb->csb_preferredDataType) && dsqlStr) + if (csb->csb_preferredDesc && ((DTYPE_IS_DECFLOAT(csb->csb_preferredDesc->dsc_dtype) || + csb->csb_preferredDesc->dsc_dtype == dtype_int128) && dsqlStr)) { const string& s(dsqlStr->getString()); dsc desc; desc.makeText(s.length(), CS_ASCII, (UCHAR*) s.c_str()); - switch (csb->csb_preferredDataType) + switch (csb->csb_preferredDesc->dsc_dtype) { case dtype_dec64: *((Decimal64*) litDesc.dsc_address) = CVT_get_dec64(&desc, tdbb->getAttachment()->att_dec_status, ERR_post); litDesc.dsc_dtype = dtype_dec64; + litDesc.dsc_scale = 0; break; case dtype_dec128: - case dtype_dec_fixed: *((Decimal128*) litDesc.dsc_address) = CVT_get_dec128(&desc, tdbb->getAttachment()->att_dec_status, ERR_post); litDesc.dsc_dtype = dtype_dec128; + litDesc.dsc_scale = 0; + break; + + case dtype_int128: + *((Int128*) litDesc.dsc_address) = CVT_get_int128(&desc, + csb->csb_preferredDesc->dsc_scale, tdbb->getAttachment()->att_dec_status, ERR_post); + litDesc.dsc_dtype = dtype_int128; + litDesc.dsc_scale = csb->csb_preferredDesc->dsc_scale; break; } } @@ -8744,8 +8741,8 @@ dsc* NegateNode::execute(thread_db* tdbb, jrd_req* request) const impure->vlu_misc.vlu_dec128 = impure->vlu_misc.vlu_dec128.neg(); break; - case dtype_dec_fixed: - impure->vlu_misc.vlu_dec_fixed = impure->vlu_misc.vlu_dec_fixed.neg(); + case dtype_int128: + impure->vlu_misc.vlu_int128 = impure->vlu_misc.vlu_int128.neg(); break; case dtype_int64: @@ -11091,7 +11088,7 @@ void SubQueryNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: desc->dsc_dtype = dtype_dec128; desc->dsc_length = sizeof(Decimal128); desc->dsc_scale = 0; diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 255953724b..aa6a4a3993 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -532,7 +532,7 @@ public: static const unsigned FLAG_DATE = 0x20; static const unsigned FLAG_DECFLOAT = 0x40; static const unsigned FLAG_VALUE = 0x80; // Full value area required in impure space. - static const unsigned FLAG_DECFIXED = 0x100; + static const unsigned FLAG_INT128 = 0x100; explicit ExprNode(Type aType, MemoryPool& pool) : DmlNode(pool), diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 198d7ed649..0fefb75f62 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -501,7 +501,7 @@ AssignmentNode* AssignmentNode::pass2(thread_db* tdbb, CompilerScratch* csb) { // scope dsc desc; asgnTo->getDesc(tdbb, csb, &desc); - AutoSetRestore dataType(&csb->csb_preferredDataType, desc.dsc_dtype); + AutoSetRestore dataType(&csb->csb_preferredDesc, &desc); ExprNode::doPass2(tdbb, csb, asgnFrom.getAddress()); } ExprNode::doPass2(tdbb, csb, asgnTo.getAddress()); @@ -8360,11 +8360,14 @@ void SetDecFloatTrapsNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_t //-------------------- -void SetDecFloatBindNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const +void SetHighPrecBindNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_tra** /*traHandle*/) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); - attachment->att_dec_binding = bind; + if (bindInt128) + attachment->att_i128_binding = bind; + else + attachment->att_dec_binding = bind; } diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index cdf6ad91f4..7a9854124f 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -30,6 +30,7 @@ #include "../dsql/Nodes.h" #include "../dsql/DdlNodes.h" #include "../dsql/NodePrinter.h" +#include "../common/DecFloat.h" namespace Jrd { @@ -1748,11 +1749,13 @@ public: }; -class SetDecFloatBindNode : public SessionManagementNode +class SetHighPrecBindNode : public SessionManagementNode { public: - SetDecFloatBindNode(MemoryPool& pool) - : SessionManagementNode(pool) + SetHighPrecBindNode(MemoryPool& pool, bool isInt128) + : SessionManagementNode(pool), + bind(Firebird::NumericBinding::NUM_NATIVE), + bindInt128(isInt128) { } @@ -1764,13 +1767,14 @@ public: NODE_PRINT(printer, bind.bind); NODE_PRINT(printer, bind.numScale); - return "SetDecFloatBindNode"; + return "SetHighPrecBindNode"; } virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const; public: - Firebird::DecimalBinding bind; + Firebird::NumericBinding bind; + bool bindInt128; }; diff --git a/src/dsql/ddl_proto.h b/src/dsql/ddl_proto.h index 0743dc947d..56b1963b3d 100644 --- a/src/dsql/ddl_proto.h +++ b/src/dsql/ddl_proto.h @@ -60,7 +60,7 @@ const USHORT blr_dtypes[] = { blr_bool, // dtype_boolean blr_dec64, // dtype_dec64 blr_dec128, // dtype_dec128 - blr_dec_fixed, // dtype_dec_fixed + blr_int128, // dtype_int128 blr_sql_time_tz, // dtype_sql_time_tz blr_timestamp_tz // dtype_timestamp_tz }; diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index dcee054350..9bc9ddf9e2 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -1148,7 +1148,7 @@ void dsql_req::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* messag desc.dsc_address = dsql_msg_buf + (IPTR) desc.dsc_address; if (notNull) - MOVD_move(tdbb, &parDesc, &desc, toExternal); + MOVD_move(tdbb, &parDesc, &desc); else memset(desc.dsc_address, 0, desc.dsc_length); } @@ -1156,7 +1156,7 @@ void dsql_req::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* messag { // Safe cast because desc is used as source only. desc.dsc_address = const_cast(in_dsql_msg_buf) + (IPTR) desc.dsc_address; - MOVD_move(tdbb, &desc, &parDesc, toExternal); + MOVD_move(tdbb, &desc, &parDesc); } else memset(parDesc.dsc_address, 0, parDesc.dsc_length); @@ -1191,7 +1191,7 @@ void dsql_req::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* messag dsc desc = parameter->par_desc; desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - MOVD_move(tdbb, &parentDesc, &desc, false); + MOVD_move(tdbb, &parentDesc, &desc); dsql_par* null_ind = parameter->par_null; if (null_ind != NULL) @@ -1221,7 +1221,7 @@ void dsql_req::mapInOut(thread_db* tdbb, bool toExternal, const dsql_msg* messag dsc desc = parameter->par_desc; desc.dsc_address = msgBuffer + (IPTR) desc.dsc_address; - MOVD_move(tdbb, &parentDesc, &desc, false); + MOVD_move(tdbb, &parentDesc, &desc); dsql_par* null_ind = parameter->par_null; if (null_ind != NULL) diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index e1fcccc593..4b2f1b2c27 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -256,8 +256,8 @@ public: precision = 18; break; - case dtype_dec_fixed: - precision = 34; + case dtype_int128: + precision = 38; break; default: diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index 2a0bd96952..39b631cf97 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -203,21 +203,24 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message) if (fromCharSet != toCharSet) parameter->par_desc.setTextType(toCharSet); } - else if (parameter->par_desc.isDecFloat()) + else if (parameter->par_desc.isDecFloat() || + parameter->par_desc.dsc_dtype == dtype_int128) { - const DecimalBinding& b = tdbb->getAttachment()->att_dec_binding; + const NumericBinding& b(parameter->par_desc.dsc_dtype == dtype_int128 ? + tdbb->getAttachment()->att_i128_binding : tdbb->getAttachment()->att_dec_binding); switch (b.bind) { - case DecimalBinding::DEC_NATIVE: + case NumericBinding::NUM_NATIVE: break; - case DecimalBinding::DEC_TEXT: + case NumericBinding::NUM_TEXT: parameter->par_desc.makeText((parameter->par_desc.dsc_dtype == dtype_dec64 ? - IDecFloat16::STRING_SIZE : IDecFloat34::STRING_SIZE) - 1, ttype_ascii); + IDecFloat16::STRING_SIZE : parameter->par_desc.dsc_dtype == dtype_dec128 ? + IDecFloat34::STRING_SIZE : IInt128::STRING_SIZE) - 1, ttype_ascii); break; - case DecimalBinding::DEC_DOUBLE: + case NumericBinding::NUM_DOUBLE: parameter->par_desc.makeDouble(); break; - case DecimalBinding::DEC_NUMERIC: + case NumericBinding::NUM_INT64: parameter->par_desc.makeInt64(b.numScale); break; } @@ -415,8 +418,8 @@ void GEN_descriptor( DsqlCompilerScratch* dsqlScratch, const dsc* desc, bool tex dsqlScratch->appendUChar(blr_dec128); break; - case dtype_dec_fixed: - dsqlScratch->appendUChar(blr_dec_fixed); + case dtype_int128: + dsqlScratch->appendUChar(blr_int128); dsqlScratch->appendUChar(desc->dsc_scale); break; diff --git a/src/dsql/movd.cpp b/src/dsql/movd.cpp index d9fbe42e60..080c1ea744 100644 --- a/src/dsql/movd.cpp +++ b/src/dsql/movd.cpp @@ -33,11 +33,11 @@ using namespace Firebird; // Move (and possible convert) something to something else. -void MOVD_move(thread_db* tdbb, dsc* from, dsc* to, bool toExternal) +void MOVD_move(thread_db* tdbb, dsc* from, dsc* to) { try { - MOV_move_ext(tdbb, from, to, toExternal); + MOV_move(tdbb, from, to); } catch (const status_exception& ex) { diff --git a/src/dsql/movd_proto.h b/src/dsql/movd_proto.h index 5a230b42c7..60f1c18288 100644 --- a/src/dsql/movd_proto.h +++ b/src/dsql/movd_proto.h @@ -24,6 +24,6 @@ #ifndef DSQL_MOVD_PROTO_H #define DSQL_MOVD_PROTO_H -void MOVD_move(Jrd::thread_db* tdbb, dsc*, dsc*, bool toExternal); +void MOVD_move(Jrd::thread_db* tdbb, dsc*, dsc*); #endif // DSQL_MOVD_PROTO_H diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 7362a43b53..6a297f6606 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -613,6 +613,7 @@ using namespace Firebird; %token HEX_ENCODE %token IDLE %token INVOKER +%token INT128 %token IV %token LAST_DAY %token LEGACY @@ -816,7 +817,7 @@ using namespace Firebird; Jrd::CreateAlterRoleNode* createAlterRoleNode; Jrd::SetDecFloatRoundNode* setDecFloatRoundNode; Jrd::SetDecFloatTrapsNode* setDecFloatTrapsNode; - Jrd::SetDecFloatBindNode* setDecFloatBindNode; + Jrd::SetHighPrecBindNode* setHighPrecBindNode; Jrd::SessionResetNode* sessionResetNode; } @@ -4224,6 +4225,7 @@ keyword_or_column | VAR_SAMP | VAR_POP | DECFLOAT // added in FB 4.0 + | INT128 | LOCAL | LOCALTIME | LOCALTIMESTAMP @@ -4971,14 +4973,14 @@ prec_scale { $$ = newNode(); - if ($2 < 1 || $2 > 34) - yyabandon(YYPOSNARG(2), -842, Arg::Gds(isc_precision_err2) << Arg::Num(1) << Arg::Num(34)); - // Precision must be between 1 and 34 + if ($2 < 1 || $2 > 38) + yyabandon(YYPOSNARG(2), -842, Arg::Gds(isc_precision_err2) << Arg::Num(1) << Arg::Num(38)); + // Precision must be between 1 and 38 if ($2 > 18) { - $$->dtype = dtype_dec_fixed; - $$->length = sizeof(DecimalFixed); + $$->dtype = dtype_int128; + $$->length = sizeof(Int128); } else if ($2 > 9) { @@ -5027,17 +5029,17 @@ prec_scale { $$ = newNode(); - if ($2 < 1 || $2 > 34) - yyabandon(YYPOSNARG(2), -842, Arg::Gds(isc_precision_err2) << Arg::Num(1) << Arg::Num(34)); - // Precision must be between 1 and 34 + if ($2 < 1 || $2 > 38) + yyabandon(YYPOSNARG(2), -842, Arg::Gds(isc_precision_err2) << Arg::Num(1) << Arg::Num(38)); + // Precision must be between 1 and 38 if ($4 > $2 || $4 < 0) yyabandon(YYPOSNARG(4), -842, isc_scale_nogt); // Scale must be between 0 and precision if ($2 > 18) { - $$->dtype = dtype_dec_fixed; - $$->length = sizeof(DecimalFixed); + $$->dtype = dtype_int128; + $$->length = sizeof(Int128); } else if ($2 > 9) { @@ -5270,14 +5272,20 @@ set_decfloat_traps { $$ = $5; } ; -%type set_decfloat_bind +%type set_decfloat_bind set_decfloat_bind - : SET DECFLOAT BIND - { $$ = newNode(); } + : SET bind_to_type BIND + { $$ = newNode($2); } decfloat_bind_clause($4) { $$ = $4; } ; +%type bind_to_type +bind_to_type + : DECFLOAT { $$ = false; } + | INT128 { $$ = true; } + ; + %type decfloat_traps_list_opt() decfloat_traps_list_opt($setDecFloatTrapsNode) : // nothing @@ -5296,26 +5304,26 @@ decfloat_trap($setDecFloatTrapsNode) { $setDecFloatTrapsNode->trap($1); } ; -%type decfloat_bind_clause() -decfloat_bind_clause($setDecFloatBindNode) +%type decfloat_bind_clause() +decfloat_bind_clause($setHighPrecBindNode) : NATIVE // do nothing | character_keyword - { $setDecFloatBindNode->bind.bind = DecimalBinding::DEC_TEXT; } + { $setHighPrecBindNode->bind.bind = NumericBinding::NUM_TEXT; } | DOUBLE PRECISION - { $setDecFloatBindNode->bind.bind = DecimalBinding::DEC_DOUBLE; } - | BIGINT decfloat_scale_clause($setDecFloatBindNode) - { $setDecFloatBindNode->bind.bind = DecimalBinding::DEC_NUMERIC; } + { $setHighPrecBindNode->bind.bind = NumericBinding::NUM_DOUBLE; } + | BIGINT decfloat_scale_clause($setHighPrecBindNode) + { $setHighPrecBindNode->bind.bind = NumericBinding::NUM_INT64; } ; -%type decfloat_scale_clause() -decfloat_scale_clause($setDecFloatBindNode) +%type decfloat_scale_clause() +decfloat_scale_clause($setHighPrecBindNode) : // nothing | ',' signed_long_integer { - if ($2 > DecimalBinding::MAX_SCALE || $2 < 0) + if ($2 > NumericBinding::MAX_SCALE || $2 < 0) yyabandon(YYPOSNARG(2), -842, isc_scale_nogt); // Scale must be between 0 and precision - $setDecFloatBindNode->bind.numScale = -$2; + $setHighPrecBindNode->bind.numScale = -$2; } %type session_statement diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 12952bc182..2bba9ee61d 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -34,6 +34,7 @@ typedef ISC_TIME_TZ; typedef ISC_TIMESTAMP_TZ; typedef FB_DEC16; typedef FB_DEC34; +typedef FB_I128; typedef isc_tr_handle; typedef isc_stmt_handle; @@ -1093,7 +1094,7 @@ interface Util : Versioned XpbBuilder getXpbBuilder(Status status, uint kind, const uchar* buf, uint len); uint setOffsets(Status status, MessageMetadata metadata, OffsetsCallback callback); -version: // 3.0 => 4.0 +version: // 3.0 => 4.0 Alpha1 DecFloat16 getDecFloat16(Status status); DecFloat34 getDecFloat34(Status status); Transaction getTransactionByHandle(Status status, isc_tr_handle* hndlPtr); @@ -1106,6 +1107,9 @@ version: // 3.0 => 4.0 uint fractions, const string timeZone); void encodeTimeStampTz(Status status, ISC_TIMESTAMP_TZ* timeStampTz, uint year, uint month, uint day, uint hours, uint minutes, uint seconds, uint fractions, const string timeZone); + +version: // 4.0 Beta1 => 4.0 Beta2 + Int128 getInt128(Status status); } interface OffsetsCallback : Versioned @@ -1515,6 +1519,15 @@ interface DecFloat34 : Versioned void fromString(Status status, const string from, FB_DEC34* to); } +interface Int128 : Versioned +{ + // - 170141183460469231731687303715884105728 e - 128 \0 + // 1 + 39 + 1 + 1 + 3 + 1 = 46 + const uint STRING_SIZE = 46; // includes terminating \0 + void toString(Status status, const FB_I128* from, int scale, uint bufferLength, string buffer); + void fromString(Status status, int scale, const string from, FB_I128* to); +} + // Replication interfaces interface ReplicatedRecord : Versioned diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index d936ede4c9..ae228568d7 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -116,6 +116,7 @@ namespace Firebird class IUdrPlugin; class IDecFloat16; class IDecFloat34; + class IInt128; class IReplicatedRecord; class IReplicatedBlob; class IReplicatedTransaction; @@ -4051,6 +4052,7 @@ namespace Firebird void (CLOOP_CARG *decodeTimeStampTz)(IUtil* self, IStatus* status, const ISC_TIMESTAMP_TZ* timeStampTz, unsigned* year, unsigned* month, unsigned* day, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions, unsigned timeZoneBufferLength, char* timeZoneBuffer) throw(); void (CLOOP_CARG *encodeTimeTz)(IUtil* self, IStatus* status, ISC_TIME_TZ* timeTz, unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions, const char* timeZone) throw(); void (CLOOP_CARG *encodeTimeStampTz)(IUtil* self, IStatus* status, ISC_TIMESTAMP_TZ* timeStampTz, unsigned year, unsigned month, unsigned day, unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions, const char* timeZone) throw(); + IInt128* (CLOOP_CARG *getInt128)(IUtil* self, IStatus* status) throw(); }; protected: @@ -4064,7 +4066,7 @@ namespace Firebird } public: - static const unsigned VERSION = 3; + static const unsigned VERSION = 4; template void getFbVersion(StatusType* status, IAttachment* att, IVersionCallback* callback) { @@ -4259,6 +4261,20 @@ namespace Firebird static_cast(this->cloopVTable)->encodeTimeStampTz(this, status, timeStampTz, year, month, day, hours, minutes, seconds, fractions, timeZone); StatusType::checkException(status); } + + template IInt128* getInt128(StatusType* status) + { + if (cloopVTable->version < 4) + { + StatusType::setVersionError(status, "IUtil", cloopVTable->version, 4); + StatusType::checkException(status); + return 0; + } + StatusType::clearException(status); + IInt128* ret = static_cast(this->cloopVTable)->getInt128(this, status); + StatusType::checkException(status); + return ret; + } }; class IOffsetsCallback : public IVersioned @@ -5856,6 +5872,45 @@ namespace Firebird } }; + class IInt128 : public IVersioned + { + public: + struct VTable : public IVersioned::VTable + { + void (CLOOP_CARG *toString)(IInt128* self, IStatus* status, const FB_I128* from, int scale, unsigned bufferLength, char* buffer) throw(); + void (CLOOP_CARG *fromString)(IInt128* self, IStatus* status, int scale, const char* from, FB_I128* to) throw(); + }; + + protected: + IInt128(DoNotInherit) + : IVersioned(DoNotInherit()) + { + } + + ~IInt128() + { + } + + public: + static const unsigned VERSION = 2; + + static const unsigned STRING_SIZE = 46; + + template void toString(StatusType* status, const FB_I128* from, int scale, unsigned bufferLength, char* buffer) + { + StatusType::clearException(status); + static_cast(this->cloopVTable)->toString(this, status, from, scale, bufferLength, buffer); + StatusType::checkException(status); + } + + template void fromString(StatusType* status, int scale, const char* from, FB_I128* to) + { + StatusType::clearException(status); + static_cast(this->cloopVTable)->fromString(this, status, scale, from, to); + StatusType::checkException(status); + } + }; + class IReplicatedRecord : public IVersioned { public: @@ -14332,6 +14387,7 @@ namespace Firebird this->decodeTimeStampTz = &Name::cloopdecodeTimeStampTzDispatcher; this->encodeTimeTz = &Name::cloopencodeTimeTzDispatcher; this->encodeTimeStampTz = &Name::cloopencodeTimeStampTzDispatcher; + this->getInt128 = &Name::cloopgetInt128Dispatcher; } } vTable; @@ -14630,6 +14686,21 @@ namespace Firebird StatusType::catchException(&status2); } } + + static IInt128* CLOOP_CARG cloopgetInt128Dispatcher(IUtil* self, IStatus* status) throw() + { + StatusType status2(status); + + try + { + return static_cast(self)->Name::getInt128(&status2); + } + catch (...) + { + StatusType::catchException(&status2); + return static_cast(0); + } + } }; template > > @@ -14666,6 +14737,7 @@ namespace Firebird virtual void decodeTimeStampTz(StatusType* status, const ISC_TIMESTAMP_TZ* timeStampTz, unsigned* year, unsigned* month, unsigned* day, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions, unsigned timeZoneBufferLength, char* timeZoneBuffer) = 0; virtual void encodeTimeTz(StatusType* status, ISC_TIME_TZ* timeTz, unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions, const char* timeZone) = 0; virtual void encodeTimeStampTz(StatusType* status, ISC_TIMESTAMP_TZ* timeStampTz, unsigned year, unsigned month, unsigned day, unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions, const char* timeZone) = 0; + virtual IInt128* getInt128(StatusType* status) = 0; }; template @@ -18081,6 +18153,73 @@ namespace Firebird virtual void fromString(StatusType* status, const char* from, FB_DEC34* to) = 0; }; + template + class IInt128BaseImpl : public Base + { + public: + typedef IInt128 Declaration; + + IInt128BaseImpl(DoNotInherit = DoNotInherit()) + { + static struct VTableImpl : Base::VTable + { + VTableImpl() + { + this->version = Base::VERSION; + this->toString = &Name::clooptoStringDispatcher; + this->fromString = &Name::cloopfromStringDispatcher; + } + } vTable; + + this->cloopVTable = &vTable; + } + + static void CLOOP_CARG clooptoStringDispatcher(IInt128* self, IStatus* status, const FB_I128* from, int scale, unsigned bufferLength, char* buffer) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::toString(&status2, from, scale, bufferLength, buffer); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + + static void CLOOP_CARG cloopfromStringDispatcher(IInt128* self, IStatus* status, int scale, const char* from, FB_I128* to) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::fromString(&status2, scale, from, to); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + }; + + template > > + class IInt128Impl : public IInt128BaseImpl + { + protected: + IInt128Impl(DoNotInherit = DoNotInherit()) + { + } + + public: + virtual ~IInt128Impl() + { + } + + virtual void toString(StatusType* status, const FB_I128* from, int scale, unsigned bufferLength, char* buffer) = 0; + virtual void fromString(StatusType* status, int scale, const char* from, FB_I128* to) = 0; + }; + template class IReplicatedRecordBaseImpl : public Base { diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h index a524012f3b..70a41ac159 100644 --- a/src/include/firebird/impl/blr.h +++ b/src/include/firebird/impl/blr.h @@ -69,7 +69,7 @@ #define blr_bool (unsigned char)23 #define blr_dec64 (unsigned char)24 #define blr_dec128 (unsigned char)25 -#define blr_dec_fixed (unsigned char)26 +#define blr_int128 (unsigned char)26 #define blr_sql_time_tz (unsigned char)28 #define blr_timestamp_tz (unsigned char)29 diff --git a/src/include/firebird/impl/consts_pub.h b/src/include/firebird/impl/consts_pub.h index 6252c49775..9f34fc29f6 100644 --- a/src/include/firebird/impl/consts_pub.h +++ b/src/include/firebird/impl/consts_pub.h @@ -129,6 +129,7 @@ #define isc_dpb_decfloat_bind 94 #define isc_dpb_decfloat_round 95 #define isc_dpb_decfloat_traps 96 +#define isc_dpb_int128_bind 97 /**************************************************/ diff --git a/src/include/firebird/impl/dsc_pub.h b/src/include/firebird/impl/dsc_pub.h index 4429673cce..bb1f3e9d12 100644 --- a/src/include/firebird/impl/dsc_pub.h +++ b/src/include/firebird/impl/dsc_pub.h @@ -63,7 +63,7 @@ #define dtype_boolean 21 #define dtype_dec64 22 #define dtype_dec128 23 -#define dtype_dec_fixed 24 +#define dtype_int128 24 #define dtype_sql_time_tz 25 #define dtype_timestamp_tz 26 #define DTYPE_TYPE_MAX 27 diff --git a/src/include/firebird/impl/sqlda_pub.h b/src/include/firebird/impl/sqlda_pub.h index 5b3f95b863..2e64e6024a 100644 --- a/src/include/firebird/impl/sqlda_pub.h +++ b/src/include/firebird/impl/sqlda_pub.h @@ -78,9 +78,9 @@ typedef struct #define SQL_TYPE_TIME 560 #define SQL_TYPE_DATE 570 #define SQL_INT64 580 +#define SQL_INT128 32752 #define SQL_TIMESTAMP_TZ 32754 #define SQL_TIME_TZ 32756 -#define SQL_DEC_FIXED 32758 #define SQL_DEC16 32760 #define SQL_DEC34 32762 #define SQL_BOOLEAN 32764 diff --git a/src/include/firebird/impl/types_pub.h b/src/include/firebird/impl/types_pub.h index 5fcc880dc2..bcd8653922 100644 --- a/src/include/firebird/impl/types_pub.h +++ b/src/include/firebird/impl/types_pub.h @@ -197,7 +197,12 @@ struct FB_DEC34_t { ISC_UINT64 fb_data[2]; }; +struct FB_I128_t { + ISC_UINT64 fb_data[2]; +}; + typedef struct FB_DEC16_t FB_DEC16; typedef struct FB_DEC34_t FB_DEC34; +typedef struct FB_I128_t FB_I128; #endif /* FIREBIRD_IMPL_TYPES_PUB_H */ diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 733f6c9133..5a73a6a30f 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -320,6 +320,7 @@ IsqlGlobals::IsqlGlobals() df16 = Firebird::UtilInterfacePtr()->getDecFloat16(&statusWrapper); df34 = Firebird::UtilInterfacePtr()->getDecFloat34(&statusWrapper); + i128 = Firebird::UtilInterfacePtr()->getInt128(&statusWrapper); } // I s q l G l o b a l s : : p r i n t f @@ -1838,7 +1839,7 @@ bool ISQL_printNumericType(const char* fieldName, const int fieldType, const int case INTEGER: case BIGINT: case DOUBLE_PRECISION: - case DEC_FIXED_TYPE: + case blr_int128: // Handle Integral subtypes NUMERIC and DECIMAL // We are ODS >= 10 and could be any Dialect @@ -1872,8 +1873,8 @@ bool ISQL_printNumericType(const char* fieldName, const int fieldType, const int case DOUBLE_PRECISION: isqlGlob.printf("NUMERIC(15, %d)", -fieldScale); break; - case DEC_FIXED_TYPE: - isqlGlob.printf("NUMERIC(34, %d)", -fieldScale); + case blr_int128: + isqlGlob.printf("NUMERIC(38, %d)", -fieldScale); break; } } @@ -2288,6 +2289,7 @@ static processing_state add_row(TEXT* tabname) double* dvalue; FB_DEC16* d64value; FB_DEC34* d128value; + FB_I128* i128value; UCHAR* boolean; ISC_QUAD* blobid; vary* avary; @@ -2361,11 +2363,27 @@ static processing_state add_row(TEXT* tabname) if ((!isqlGlob.df16) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) { STDERROUT("Input parsing problem"); + ISQL_errmsg(fbStatus); + done = true; + } + break; + + case SQL_INT128: + scale = msg->getScale(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + + i128value = (FB_I128*) datap; + if (isqlGlob.i128) + isqlGlob.i128->fromString(fbStatus, scale, lastInputLine, i128value); + if ((!isqlGlob.i128) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) + { + STDERROUT("Input parsing problem"); + ISQL_errmsg(fbStatus); done = true; } break; - case SQL_DEC_FIXED: case SQL_DEC34: d128value = (FB_DEC34*) datap; if (isqlGlob.df34) @@ -2373,6 +2391,7 @@ static processing_state add_row(TEXT* tabname) if ((!isqlGlob.df34) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) { STDERROUT("Input parsing problem"); + ISQL_errmsg(fbStatus); done = true; } break; @@ -3068,6 +3087,7 @@ static processing_state bulk_insert_hack(const char* command) tm times; FB_DEC16* d64value; FB_DEC34* d128value; + FB_I128* i128value; // Initialize the time structure. memset(×, 0, sizeof(times)); char msec_str[5] = ""; @@ -3134,18 +3154,38 @@ static processing_state bulk_insert_hack(const char* command) if ((!isqlGlob.df16) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) { STDERROUT("Input parsing problem"); + ISQL_errmsg(fbStatus); done = true; } break; case SQL_DEC34: - case SQL_DEC_FIXED: d128value = (FB_DEC34*) datap; if (isqlGlob.df34) isqlGlob.df34->fromString(fbStatus, get_numeric_value(lastPos).c_str(), d128value); if ((!isqlGlob.df34) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) { STDERROUT("Input parsing problem"); + ISQL_errmsg(fbStatus); + done = true; + } + break; + + case SQL_INT128: + scale = message->getScale(fbStatus, i); + if (ISQL_errmsg(fbStatus)) + return (SKIP); + + i128value = (FB_I128*) datap; + if (isqlGlob.i128) + { + isqlGlob.i128->fromString(fbStatus, scale, + get_numeric_value(lastPos).c_str(), i128value); + } + if ((!isqlGlob.i128) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) + { + STDERROUT("Input parsing problem"); + ISQL_errmsg(fbStatus); done = true; } break; @@ -7312,7 +7352,6 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length) break; case SQL_DEC34: - case SQL_DEC_FIXED: { char decStr[Firebird::IDecFloat34::STRING_SIZE]; if (isqlGlob.df34) @@ -7325,9 +7364,34 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length) strcpy(decStr, convErr); if (setValues.List) - isqlGlob.printf("%*.*s%s", sizeof(decStr) - 1, sizeof(decStr) - 1, decStr, NEWLINE); + { + isqlGlob.printf("%*.*s%s", int(sizeof(decStr) - 1), int(sizeof(decStr) - 1), + decStr, NEWLINE); + } else - sprintf(p, "%*.*s ", sizeof(decStr) - 1, sizeof(decStr) - 1, decStr); + sprintf(p, "%*.*s ", int(sizeof(decStr) - 1), int(sizeof(decStr) - 1), decStr); + } + break; + + case SQL_INT128: + { + char intStr[Firebird::IInt128::STRING_SIZE]; + if (isqlGlob.i128) + { + isqlGlob.i128->toString(fbStatus, var->value.asInt128, dscale, sizeof(intStr), intStr); + if (ISQL_errmsg(fbStatus)) + strcpy(intStr, convErr); + } + else + strcpy(intStr, convErr); + + if (setValues.List) + { + isqlGlob.printf("%*.*s%s", int(sizeof(intStr) - 1), int(sizeof(intStr) - 1), + intStr, NEWLINE); + } + else + sprintf(p, "%*.*s ", int(sizeof(intStr) - 1), int(sizeof(intStr) - 1), intStr); } break; @@ -8169,7 +8233,9 @@ static unsigned process_message_display(Firebird::IMessageMetadata* message, uns case SQL_DEC16: disp_length = Firebird::IDecFloat16::STRING_SIZE - 1; break; - case SQL_DEC_FIXED: + case SQL_INT128: + disp_length = Firebird::IInt128::STRING_SIZE - 1; + break; case SQL_DEC34: disp_length = Firebird::IDecFloat34::STRING_SIZE - 1; break; @@ -8847,8 +8913,8 @@ static const char* sqltype_to_string(unsigned sqltype) return "DECFLOAT(16)"; case SQL_DEC34: return "DECFLOAT(34)"; - case SQL_DEC_FIXED: - return "DECIMAL FIXED"; + case SQL_INT128: + return "NUMERIC(38)"; case SQL_D_FLOAT: return "D_FLOAT"; case SQL_TIMESTAMP: diff --git a/src/isql/isql.h b/src/isql/isql.h index 17e15049a1..ea239e6ecd 100644 --- a/src/isql/isql.h +++ b/src/isql/isql.h @@ -306,7 +306,6 @@ const int BIGINT = 16; const int BOOLEAN_TYPE = 23; const int DEC64_TYPE = 24; const int DEC128_TYPE = 25; -const int DEC_FIXED_TYPE = 26; static const sqltypes Column_types[] = { {SMALLINT, "SMALLINT"}, // keyword @@ -326,7 +325,7 @@ static const sqltypes Column_types[] = { {BOOLEAN_TYPE, "BOOLEAN"}, // keyword {DEC64_TYPE, "DECFLOAT(16)"}, {DEC128_TYPE, "DECFLOAT(34)"}, - {DEC_FIXED_TYPE, ""}, + {blr_int128, "INT64"}, {blr_sql_time_tz, "TIME WITH TIME ZONE"}, // keyword {blr_timestamp_tz, "TIMESTAMP WITH TIME ZONE"}, // keyword {0, ""} @@ -417,6 +416,7 @@ public: USHORT att_charset; Firebird::IDecFloat16* df16; Firebird::IDecFloat34* df34; + Firebird::IInt128* i128; void printf(const char* buffer, ...); void prints(const char* buffer); @@ -478,6 +478,7 @@ struct IsqlVar char* asChar; FB_DEC16* asDec16; FB_DEC34* asDec34; + FB_I128* asInt128; void* setPtr; }; TypeMix value; diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index f23b92f11c..798a69cc41 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -243,7 +243,7 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb, const InitialOption att_internal(*pool), att_dyn_req(*pool), att_dec_status(DecimalStatus::DEFAULT), - att_dec_binding(DecimalBinding::DEFAULT), + att_dec_binding(NumericBinding::DEFAULT), att_charsets(*pool), att_charset_ids(*pool), att_pools(*pool), diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index f92420890d..b4c47f434e 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -363,8 +363,11 @@ public: void resetAttachment(Attachment* attachment) const; private: + void setBinding(Firebird::string option, Firebird::NumericBinding& bind); + Firebird::DecimalStatus decFloatStatus = Firebird::DecimalStatus::DEFAULT; - Firebird::DecimalBinding decFloatBinding = Firebird::DecimalBinding::DEFAULT; + Firebird::NumericBinding decFloatBinding = Firebird::NumericBinding::DEFAULT; + Firebird::NumericBinding int128Binding = Firebird::NumericBinding::DEFAULT; Firebird::TimeZoneUtil::Bind timeZoneBind = Firebird::TimeZoneUtil::BIND_NATIVE; USHORT originalTimeZone = Firebird::TimeZoneUtil::GMT_ZONE; @@ -466,7 +469,8 @@ public: Firebird::Array att_dyn_req; // internal dyn statements Firebird::ICryptKeyCallback* att_crypt_callback; // callback for DB crypt Firebird::DecimalStatus att_dec_status; // error handling and rounding - Firebird::DecimalBinding att_dec_binding; // use legacy datatype for DecFloat in outer world + Firebird::NumericBinding att_dec_binding; // use legacy datatype for DecFloat in outer world + Firebird::NumericBinding att_i128_binding; // use legacy datatype for INT128 in outer world jrd_req* findSystemRequest(thread_db* tdbb, USHORT id, USHORT which); diff --git a/src/jrd/ExtEngineManager.cpp b/src/jrd/ExtEngineManager.cpp index cbf5e69cb3..fcea4ec0ef 100644 --- a/src/jrd/ExtEngineManager.cpp +++ b/src/jrd/ExtEngineManager.cpp @@ -1570,9 +1570,6 @@ void ExtEngineManager::makeTrigger(thread_db* tdbb, CompilerScratch* csb, Jrd::T if (field) { dsc d(relFormat->fmt_desc[i]); - if (d.dsc_dtype == dtype_dec_fixed) - d.dsc_dtype = dtype_dec128; - fieldsMsg->addItem(field->fld_name, !field->fld_not_null, d); } } diff --git a/src/jrd/PreparedStatement.cpp b/src/jrd/PreparedStatement.cpp index 4bcf02b7c7..fb98f5bf30 100644 --- a/src/jrd/PreparedStatement.cpp +++ b/src/jrd/PreparedStatement.cpp @@ -109,10 +109,10 @@ namespace item.length = sizeof(Decimal128); break; - case dtype_dec_fixed: - item.type = SQL_DEC_FIXED; + case dtype_int128: + item.type = SQL_INT128; item.scale = desc->dsc_scale; - item.length = sizeof(DecimalFixed); + item.length = sizeof(Int128); break; case dtype_sql_date: diff --git a/src/jrd/Routine.cpp b/src/jrd/Routine.cpp index bbefc73e88..2a88bd4f9e 100644 --- a/src/jrd/Routine.cpp +++ b/src/jrd/Routine.cpp @@ -44,7 +44,7 @@ MsgMetadata* Routine::createMetadata(const Array >& paramet ++i) { dsc d((*i)->prm_desc); - if (isExtern && d.dsc_dtype == dtype_dec_fixed) + if (isExtern && d.dsc_dtype == dtype_int128) d.dsc_dtype = dtype_dec128; metadata->addItem((*i)->prm_name, (*i)->prm_nullable, d); } diff --git a/src/jrd/align.h b/src/jrd/align.h index e8be9e6468..c7acb0331d 100644 --- a/src/jrd/align.h +++ b/src/jrd/align.h @@ -33,6 +33,7 @@ Maximum alignments for corresponding data types are defined in dsc.h */ #include "../common/DecFloat.h" +#include "../common/Int128.h" #include "firebird/impl/blr.h" /* The following macro must be defined as the highest-numericly-valued @@ -70,7 +71,7 @@ static const USHORT gds_cvt_blr_dtype[DTYPE_BLR_MAX + 1] = dtype_boolean, // blr_bool == 23 dtype_dec64, /* blr_dec64 == 24 */ dtype_dec128, /* blr_dec128 == 25 */ - dtype_dec_fixed, /* blr_dec_fixed == 26 */ + dtype_int128, /* blr_int128 == 26 */ dtype_double, /* blr_double == 27 */ dtype_sql_time_tz, /* blr_sql_time_tz == 28 */ dtype_timestamp_tz, /* blr_timestamp_tz == 29 */ @@ -111,7 +112,7 @@ static const USHORT type_alignments[DTYPE_TYPE_MAX] = sizeof(UCHAR), /* dtype_boolean */ sizeof(Firebird::Decimal64),/* dtype_dec64 */ sizeof(Firebird::Decimal64),/* dtype_dec128 */ - sizeof(Firebird::Decimal64),/* dtype_dec_fixed */ + sizeof(Firebird::Decimal64),/* dtype_int128 */ sizeof(GDS_TIME), /* dtype_sql_time_tz */ sizeof(GDS_DATE) /* dtype_timestamp_tz */ }; @@ -142,7 +143,7 @@ static const USHORT type_lengths[DTYPE_TYPE_MAX] = sizeof(UCHAR), /* dtype_boolean */ sizeof(Firebird::Decimal64),/* dtype_dec64 */ sizeof(Firebird::Decimal128),/*dtype_dec128 */ - sizeof(Firebird::DecimalFixed), /* dtype_dec_fixed */ + sizeof(Firebird::Int128), /* dtype_int128 */ sizeof(ISC_TIME_TZ), /* dtype_sql_time_tz */ sizeof(ISC_TIMESTAMP_TZ) /* dtype_timestamp_tz */ }; @@ -176,7 +177,7 @@ static const USHORT type_significant_bits[DTYPE_TYPE_MAX] = 0, // dtype_boolean 0, // dtype_dec64 0, // dtype_dec128 - 0, // dtype_dec_fixed + 0, // dtype_int128 0, // dtype_sql_time_tz 0 // dtype_timestamp_tz }; diff --git a/src/jrd/cvt.cpp b/src/jrd/cvt.cpp index 0a9d2031ef..6ccd886402 100644 --- a/src/jrd/cvt.cpp +++ b/src/jrd/cvt.cpp @@ -223,19 +223,20 @@ UCHAR CVT_get_numeric(const UCHAR* string, const USHORT length, SSHORT* scale, v // tricky: the value doesn't always become negative after an // overflow! - if (value >= NUMERIC_LIMIT) + if (!over) { - // possibility of an overflow - if ((value > NUMERIC_LIMIT) || (*p > '8' && sign == -1) || (*p > '7' && sign != -1)) + if (value >= NUMERIC_LIMIT) { - over = true; - break; + // possibility of an overflow + if ((value > NUMERIC_LIMIT) || (*p > '8' && sign == -1) || (*p > '7' && sign != -1)) + over = true; } + + // Force the subtraction to be performed before the addition, + // thus preventing a possible signed arithmetic overflow. + value = value * 10 + (*p - '0'); } - // Force the subtraction to be performed before the addition, - // thus preventing a possible signed arithmetic overflow. - value = value * 10 + (*p - '0'); if (fraction) --local_scale; } @@ -262,6 +263,8 @@ UCHAR CVT_get_numeric(const UCHAR* string, const USHORT length, SSHORT* scale, v if ((local_scale > MAX_SCHAR) || (local_scale < MIN_SCHAR)) over = true; + *scale = local_scale; + if ((!over) && ((p < end) || // there is an exponent ((value < 0) && (sign != -1)))) // MAX_SINT64+1 wrapped around { @@ -278,8 +281,6 @@ UCHAR CVT_get_numeric(const UCHAR* string, const USHORT length, SSHORT* scale, v return dtype_dec128; } - *scale = local_scale; - // The literal has already been converted to a 64-bit integer: return // a long if the value fits into a long, else return an int64. diff --git a/src/jrd/cvt2.cpp b/src/jrd/cvt2.cpp index 96f2e24260..8959088287 100644 --- a/src/jrd/cvt2.cpp +++ b/src/jrd/cvt2.cpp @@ -85,7 +85,7 @@ const BYTE CVT2_compare_priority[] = 9, // dtype_long // Move quad up by one to make room for int64 at its proper place in the table. 11, // dtype_quad - // Also leave space for dec_fixed, dec64 and dec 128. + // Also leave space for int128, dec64 and dec 128. 15, // dtype_real 16, // dtype_double 17, // dtype_d_float @@ -99,8 +99,8 @@ const BYTE CVT2_compare_priority[] = 10, // dtype_int64 - goes right after long 25, // dtype_dbkey - compares with nothing except itself 26, // dtype_boolean - compares with nothing except itself - 12, // dtype_dec_fixed - go after quad - 13, // dec64 - go after dtype_dec_fixed + 12, // dtype_int128 - go after quad + 13, // dec64 - go after dtype_int128 14, // dec128 - go after dec64 and before real 20, // dtype_sql_time_tz - go after dtype_sql_time 22 // dtype_timestamp_tz - go after dtype_timestamp @@ -305,8 +305,8 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt case dtype_dec128: return ((Decimal128*) p1)->compare(decSt, *(Decimal128*) p2); - case dtype_dec_fixed: - return ((DecimalFixed*) p1)->compare(decSt, *(DecimalFixed*) p2); + case dtype_int128: + return ((Int128*) p1)->compare(*(Int128*) p2); case dtype_boolean: return *p1 == *p2 ? 0 : *p1 < *p2 ? -1 : 1; @@ -564,7 +564,7 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt return temp1.compare(decSt, temp2); } - case dtype_dec_fixed: + case dtype_int128: { SSHORT scale; if (arg2->dsc_dtype > dtype_varying) @@ -572,9 +572,9 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt else scale = arg1->dsc_scale; - const DecimalFixed temp1 = CVT_get_dec_fixed(arg1, scale, decSt, ERR_post); - const DecimalFixed temp2 = CVT_get_dec_fixed(arg2, scale, decSt, ERR_post); - return temp1.compare(decSt, temp2); + const Int128 temp1 = CVT_get_int128(arg1, scale, decSt, ERR_post); + const Int128 temp2 = CVT_get_int128(arg2, scale, decSt, ERR_post); + return temp1.compare(temp2); } case dtype_blob: diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 367c492aea..ce4c7dced0 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1302,7 +1302,7 @@ USHORT DFW_assign_index_type(thread_db* tdbb, const Firebird::MetaName& name, SS return idx_boolean; case dtype_dec64: case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: return idx_decimal; default: return idx_numeric; diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 64ea89226b..a25cae27a3 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -432,8 +432,8 @@ void EVL_make_value(thread_db* tdbb, const dsc* desc, impure_value* value, Memor value->vlu_misc.vlu_dec128 = *((Decimal128*) from.dsc_address); return; - case dtype_dec_fixed: - value->vlu_misc.vlu_dec_fixed = *((DecimalFixed*) from.dsc_address); + case dtype_int128: + value->vlu_misc.vlu_int128 = *((Int128*) from.dsc_address); return; case dtype_sql_time: diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 2c6514486e..330545f533 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -64,7 +64,6 @@ DEFINE_TRACE_ROUTINE(cmp_trace); #endif class VaryingString; -struct dsc; namespace Jrd { @@ -463,7 +462,7 @@ public: csb_currentForNode(NULL), csb_currentDMLNode(NULL), csb_currentAssignTarget(NULL), - csb_preferredDataType(0), + csb_preferredDesc(NULL), csb_rpt(p) { csb_dbg_info = FB_NEW_POOL(p) Firebird::DbgInfo(p); @@ -546,7 +545,7 @@ public: ForNode* csb_currentForNode; StmtNode* csb_currentDMLNode; // could be StoreNode or ModifyNode ExprNode* csb_currentAssignTarget; - UCHAR csb_preferredDataType; // expected by receiving side datatype + dsc* csb_preferredDesc; // expected by receiving side data format struct csb_repeat { diff --git a/src/jrd/fun.epp b/src/jrd/fun.epp index c5aee2f1fc..e1a9ae1cc7 100644 --- a/src/jrd/fun.epp +++ b/src/jrd/fun.epp @@ -606,7 +606,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra break; case dtype_dec128: - case dtype_dec_fixed: + case dtype_int128: { const Decimal128 d = MOV_get_dec128(tdbb, input); if (parameter->prm_fun_mechanism == FUN_value) @@ -820,21 +820,6 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra Arg::Str(function->getName().toString())); } break; - - case dtype_dec_fixed: - if (value->vlu_misc.vlu_dec_fixed.isInf()) - { - status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_udf_fp_overflow) << - Arg::Str(function->getName().toString())); - } - else if (value->vlu_misc.vlu_dec_fixed.isNan()) - { - status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_udf_fp_nan) << - Arg::Str(function->getName().toString())); - } - break; } request->req_flags &= ~req_null; @@ -1148,8 +1133,8 @@ static void invoke(thread_db* tdbb, value->vlu_misc.vlu_dec128 = CALL_UDF(tdbb, function->fun_entrypoint, args); break; - case dtype_dec_fixed: - value->vlu_misc.vlu_dec_fixed = CALL_UDF(tdbb, function->fun_entrypoint, args); + case dtype_int128: + value->vlu_misc.vlu_int128 = CALL_UDF(tdbb, function->fun_entrypoint, args); break; case dtype_timestamp: diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 08fb2f2330..afb052bbba 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -1020,6 +1020,7 @@ namespace Jrd string dpb_decfloat_bind; string dpb_decfloat_round; string dpb_decfloat_traps; + string dpb_int128_bind; public: static const ULONG DPB_FLAGS_MASK = DBB_damaged; @@ -1062,6 +1063,43 @@ namespace Jrd } }; + void Attachment::InitialOptions::setBinding(string option, NumericBinding& bind) + { + option.lower(); + + if (option == "native") + bind = NumericBinding::DEFAULT; + else if (option == "char" || option == "character") + bind = NumericBinding(NumericBinding::NUM_TEXT); + else if (option == "double" || option == "double precision") + bind = NumericBinding(NumericBinding::NUM_DOUBLE); + else if (option == "bigint") + bind = NumericBinding(NumericBinding::NUM_INT64); + else if (option.substr(0, 7) == "bigint,") + { + const char* p = option.c_str() + 7; + + while (*p == ' ') + ++p; + + const char* start = p; + int scale = 0; + + while (*p >= '0' && *p <= '9') + { + scale = scale * 10 + (*p - '0'); + ++p; + } + + if (*p != '\0' || p - start == 0 || p - start > 2 || scale > NumericBinding::MAX_SCALE) + (Arg::Gds(isc_invalid_decfloat_bind) << option).raise(); + + bind = NumericBinding(NumericBinding::NUM_INT64, static_cast(-scale)); + } + else + (Arg::Gds(isc_invalid_decfloat_bind) << option).raise(); + } + Attachment::InitialOptions::InitialOptions(const DatabaseOptions& options) { if (options.dpb_time_zone_bind.hasData()) @@ -1078,42 +1116,10 @@ namespace Jrd } if (options.dpb_decfloat_bind.hasData()) - { - auto option = options.dpb_decfloat_bind; - option.lower(); + setBinding(options.dpb_decfloat_bind, decFloatBinding); - if (option == "native") - decFloatBinding = DecimalBinding::DEFAULT; - else if (option == "char" || option == "character") - decFloatBinding = DecimalBinding(DecimalBinding::DEC_TEXT); - else if (option == "double" || option == "double precision") - decFloatBinding = DecimalBinding(DecimalBinding::DEC_DOUBLE); - else if (option == "bigint") - decFloatBinding = DecimalBinding(DecimalBinding::DEC_NUMERIC); - else if (option.substr(0, 7) == "bigint,") - { - const char* p = option.c_str() + 7; - - while (*p == ' ') - ++p; - - const char* start = p; - int scale = 0; - - while (*p >= '0' && *p <= '9') - { - scale = scale * 10 + (*p - '0'); - ++p; - } - - if (*p != '\0' || p - start == 0 || p - start > 2 || scale > DecimalBinding::MAX_SCALE) - (Arg::Gds(isc_invalid_decfloat_bind) << option).raise(); - - decFloatBinding = DecimalBinding(DecimalBinding::DEC_NUMERIC, static_cast(-scale)); - } - else - (Arg::Gds(isc_invalid_decfloat_bind) << option).raise(); - } + if (options.dpb_int128_bind.hasData()) + setBinding(options.dpb_int128_bind, int128Binding); if (options.dpb_decfloat_round.hasData()) { @@ -1172,6 +1178,7 @@ namespace Jrd // reset DecFloat options attachment->att_dec_status = decFloatStatus; attachment->att_dec_binding = decFloatBinding; + attachment->att_i128_binding = int128Binding; // reset time zone options attachment->att_timezone_bind = timeZoneBind; @@ -6969,6 +6976,10 @@ void DatabaseOptions::get(const UCHAR* dpb, USHORT dpb_length, bool& invalid_cli rdr.getString(dpb_decfloat_bind); break; + case isc_dpb_int128_bind: + rdr.getString(dpb_int128_bind); + break; + case isc_dpb_decfloat_round: rdr.getString(dpb_decfloat_round); break; diff --git a/src/jrd/mov.cpp b/src/jrd/mov.cpp index ad15676f28..9c10a83603 100644 --- a/src/jrd/mov.cpp +++ b/src/jrd/mov.cpp @@ -450,42 +450,6 @@ void MOV_move(Jrd::thread_db* tdbb, /*const*/ dsc* from, dsc* to) } -void MOV_move_ext(Jrd::thread_db* tdbb, /*const*/ dsc* from, dsc* to, bool toExtern) -{ -/************************************** - * - * M O V _ m o v e _ e x t - * - ************************************** - * - * Functional description - * Move data to/from outer world. - * - **************************************/ - - MOV_move(tdbb, from, to); - - switch (to->dsc_dtype) - { - case dtype_dec_fixed: - if (toExtern) - { - ((Decimal128*) to->dsc_address)->setScale(tdbb->getAttachment()->att_dec_status, - to->dsc_scale); - } - else - { - ((DecimalFixed*) to->dsc_address)->exactInt(tdbb->getAttachment()->att_dec_status, - to->dsc_scale); - } - break; - - default: - break; - } -} - - Decimal64 MOV_get_dec64(Jrd::thread_db* tdbb, const dsc* desc) { /************************************** @@ -510,7 +474,7 @@ Decimal128 MOV_get_dec128(Jrd::thread_db* tdbb, const dsc* desc) } -DecimalFixed MOV_get_dec_fixed(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale) +Int128 MOV_get_int128(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale) { /************************************** * @@ -518,7 +482,7 @@ DecimalFixed MOV_get_dec_fixed(Jrd::thread_db* tdbb, const dsc* desc, SSHORT sca * **************************************/ - return CVT_get_dec_fixed(desc, scale, tdbb->getAttachment()->att_dec_status, ERR_post); + return CVT_get_int128(desc, scale, tdbb->getAttachment()->att_dec_status, ERR_post); } diff --git a/src/jrd/mov_proto.h b/src/jrd/mov_proto.h index 31f59d458c..4e600889ab 100644 --- a/src/jrd/mov_proto.h +++ b/src/jrd/mov_proto.h @@ -52,10 +52,9 @@ int MOV_make_string2(Jrd::thread_db*, const dsc*, USHORT, UCHAR**, Jrd::MoveBuf Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT ttype, bool limit = true); void MOV_move(Jrd::thread_db*, /*const*/ dsc*, dsc*); -void MOV_move_ext(Jrd::thread_db* tdbb, /*const*/ dsc* from, dsc* to, bool toExtern); Firebird::Decimal64 MOV_get_dec64(Jrd::thread_db*, const dsc*); Firebird::Decimal128 MOV_get_dec128(Jrd::thread_db*, const dsc*); -Firebird::DecimalFixed MOV_get_dec_fixed(Jrd::thread_db*, const dsc*, SSHORT); +Firebird::Int128 MOV_get_int128(Jrd::thread_db*, const dsc*, SSHORT); namespace Jrd { diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp index ec23786b71..7c5866bbbf 100644 --- a/src/jrd/opt.cpp +++ b/src/jrd/opt.cpp @@ -419,7 +419,7 @@ static const UCHAR sort_dtypes[] = SKD_bytes, // dtype_boolean SKD_dec64, // dtype_dec64 SKD_dec128, // dtype_dec128 - SKD_dec128, // dtype_dec_fixed + SKD_int128, // dtype_int128 SKD_sql_time_tz, // dtype_sql_time_tz SKD_timestamp_tz // dtype_timestamp_tz }; diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index c4dada62e8..a0eabbe1bf 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -405,9 +405,9 @@ USHORT PAR_datatype(BlrReader& blrReader, dsc* desc) desc->dsc_length = sizeof(Decimal128); break; - case blr_dec_fixed: - desc->dsc_dtype = dtype_dec_fixed; - desc->dsc_length = sizeof(DecimalFixed); + case blr_int128: + desc->dsc_dtype = dtype_int128; + desc->dsc_length = sizeof(Int128); desc->dsc_scale = (int) blrReader.getByte(); break; diff --git a/src/jrd/sort.cpp b/src/jrd/sort.cpp index 947db4d47b..f893fb044d 100644 --- a/src/jrd/sort.cpp +++ b/src/jrd/sort.cpp @@ -804,6 +804,7 @@ void Sort::diddleKey(UCHAR* record, bool direction, bool duplicateHandling) case SKD_timestamp: case SKD_sql_date: case SKD_int64: + case SKD_int128: *p ^= 1 << 7; break; @@ -1047,6 +1048,24 @@ void Sort::diddleKey(UCHAR* record, bool direction, bool duplicateHandling) SWAP_LONGS(lwp[0], lwp[1], lw); break; + case SKD_int128: + // INT128 fits in four long, and hence two swaps should happen + // here for the right order comparison using DO_32_COMPARE + if (!direction) + { + SWAP_LONGS(lwp[0], lwp[3], lw); + SWAP_LONGS(lwp[1], lwp[2], lw); + } + + p[15] ^= 1 << 7; + + if (direction) + { + SWAP_LONGS(lwp[0], lwp[3], lw); + SWAP_LONGS(lwp[1], lwp[2], lw); + } + break; + #ifdef IEEE case SKD_double: if (!direction) diff --git a/src/jrd/sort.h b/src/jrd/sort.h index 47a72abd7e..eade49d674 100644 --- a/src/jrd/sort.h +++ b/src/jrd/sort.h @@ -145,6 +145,7 @@ const int SKD_dec64 = 16; const int SKD_dec128 = 17; const int SKD_sql_time_tz = 18; const int SKD_timestamp_tz = 19; +const int SKD_int128 = 20; // skd_flags const UCHAR SKD_ascending = 0; // default initializer diff --git a/src/jrd/val.h b/src/jrd/val.h index bbbb263278..0db02aacce 100644 --- a/src/jrd/val.h +++ b/src/jrd/val.h @@ -85,7 +85,7 @@ struct impure_value double vlu_double; Firebird::Decimal64 vlu_dec64; Firebird::Decimal128 vlu_dec128; - Firebird::DecimalFixed vlu_dec_fixed; + Firebird::Int128 vlu_int128; GDS_TIMESTAMP vlu_timestamp; ISC_TIMESTAMP_TZ vlu_timestamp_tz; GDS_TIME vlu_sql_time; @@ -101,7 +101,7 @@ struct impure_value void make_int64(const SINT64 val, const signed char scale = 0); void make_double(const double val); void make_decimal128(const Firebird::Decimal128 val); - void make_decimal_fixed(const Firebird::DecimalFixed val, const signed char scale); + void make_decimal_fixed(const Firebird::Int128 val, const signed char scale); }; // Do not use these methods where dsc_sub_type is not explicitly set to zero. @@ -145,14 +145,14 @@ inline void impure_value::make_decimal128(const Firebird::Decimal128 val) this->vlu_desc.dsc_address = reinterpret_cast(&this->vlu_misc.vlu_dec128); } -inline void impure_value::make_decimal_fixed(const Firebird::DecimalFixed val, const signed char scale) +inline void impure_value::make_decimal_fixed(const Firebird::Int128 val, const signed char scale) { - this->vlu_misc.vlu_dec_fixed = val; - this->vlu_desc.dsc_dtype = dtype_dec_fixed; - this->vlu_desc.dsc_length = sizeof(Firebird::DecimalFixed); + this->vlu_misc.vlu_int128 = val; + this->vlu_desc.dsc_dtype = dtype_int128; + this->vlu_desc.dsc_length = sizeof(Firebird::Int128); this->vlu_desc.dsc_scale = scale; this->vlu_desc.dsc_sub_type = 0; - this->vlu_desc.dsc_address = reinterpret_cast(&this->vlu_misc.vlu_dec_fixed); + this->vlu_desc.dsc_address = reinterpret_cast(&this->vlu_misc.vlu_int128); } struct impure_value_ex : public impure_value diff --git a/src/misc/pascal/Pascal.interface.pas b/src/misc/pascal/Pascal.interface.pas index ec5d396964..cf9ef252e0 100644 --- a/src/misc/pascal/Pascal.interface.pas +++ b/src/misc/pascal/Pascal.interface.pas @@ -17,7 +17,7 @@ ISC_QUAD = array [1..2] of Integer; FB_DEC16 = array [1..1] of Int64; FB_DEC34 = array [1..2] of Int64; - FB_DEC_FIXED = array [1..2] of Int64; + FB_INT128 = array [1..2] of Int64; isc_tr_handle = ^integer32; isc_stmt_handle = ^integer32; diff --git a/src/remote/client/BlrFromMessage.cpp b/src/remote/client/BlrFromMessage.cpp index e486d37377..4f1f127fed 100644 --- a/src/remote/client/BlrFromMessage.cpp +++ b/src/remote/client/BlrFromMessage.cpp @@ -127,10 +127,10 @@ void BlrFromMessage::buildBlr(IMessageMetadata* metadata) dtype = dtype_dec128; break; - case SQL_DEC_FIXED: - appendUChar(blr_dec_fixed); + case SQL_INT128: + appendUChar(blr_int128); appendUChar(scale); - dtype = dtype_dec_fixed; + dtype = dtype_int128; break; case SQL_DOUBLE: diff --git a/src/remote/parser.cpp b/src/remote/parser.cpp index de7c3a76af..13cb500afa 100644 --- a/src/remote/parser.cpp +++ b/src/remote/parser.cpp @@ -300,11 +300,11 @@ static rem_fmt* parse_format(const UCHAR*& blr, size_t& blr_length) align = type_alignments[dtype_dec128]; break; - case blr_dec_fixed: - desc->dsc_dtype = dtype_dec_fixed; - desc->dsc_length = sizeof(DecimalFixed); + case blr_int128: + desc->dsc_dtype = dtype_int128; + desc->dsc_length = sizeof(Int128); desc->dsc_scale = *blr++; - align = type_alignments[dtype_dec_fixed]; + align = type_alignments[dtype_int128]; break; // this case cannot occur as switch paramater is char and blr_blob diff --git a/src/utilities/ntrace/TracePluginImpl.cpp b/src/utilities/ntrace/TracePluginImpl.cpp index 5f411df60f..f9d0bd7e97 100644 --- a/src/utilities/ntrace/TracePluginImpl.cpp +++ b/src/utilities/ntrace/TracePluginImpl.cpp @@ -762,7 +762,7 @@ void TracePluginImpl::appendParams(ITraceParams* params) case dtype_dec128: paramtype = "decfloat(34)"; break; - case dtype_dec_fixed: + case dtype_int128: paramtype = "decimal"; break; @@ -887,11 +887,10 @@ void TracePluginImpl::appendParams(ITraceParams* params) ((Decimal128*) parameters->dsc_address)->toString(paramvalue); break; - case dtype_dec_fixed: + case dtype_int128: try { - DecimalStatus decSt(FB_DEC_Errors); - ((DecimalFixed*) parameters->dsc_address)->toString(decSt, parameters->dsc_scale, paramvalue); + ((Int128*) parameters->dsc_address)->toString(parameters->dsc_scale, paramvalue); } catch (const Exception& ex) { diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index ec52aa27f9..013bbe9adc 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -686,6 +686,7 @@ public: void encodeTimeStampTz(Firebird::CheckStatusWrapper* status, ISC_TIMESTAMP_TZ* timeStampTz, unsigned year, unsigned month, unsigned day, unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions, const char* timeZone); + Firebird::IInt128* getInt128(Firebird::CheckStatusWrapper* status); }; } // namespace Why diff --git a/src/yvalve/gds.cpp b/src/yvalve/gds.cpp index 44282a7438..c0dd855594 100644 --- a/src/yvalve/gds.cpp +++ b/src/yvalve/gds.cpp @@ -60,6 +60,7 @@ #include "../common/classes/TempFile.h" #include "../common/utils_proto.h" #include "../common/ThreadStart.h" +#include "../common/Int128.h" #ifdef HAVE_UNISTD_H #include @@ -3034,9 +3035,9 @@ static int blr_print_dtype(gds_ctl* control) length = sizeof(Firebird::Decimal64); break; - case blr_dec_fixed: - string = "dec_fixed"; - length = sizeof(Firebird::DecimalFixed); + case blr_int128: + string = "int128"; + length = sizeof(Firebird::Int128); break; case blr_domain_name: @@ -3100,7 +3101,7 @@ static int blr_print_dtype(gds_ctl* control) case blr_long: case blr_quad: case blr_int64: - case blr_dec_fixed: + case blr_int128: blr_print_byte(control); break; diff --git a/src/yvalve/utl.cpp b/src/yvalve/utl.cpp index 7a4ab4fd76..8d0f32c34a 100644 --- a/src/yvalve/utl.cpp +++ b/src/yvalve/utl.cpp @@ -1303,6 +1303,49 @@ IDecFloat34* UtilInterface::getDecFloat34(CheckStatusWrapper* status) return &decFloat34; } +class IfaceInt128 FB_FINAL : public AutoIface > +{ +public: + // IInt128 implementation + void toString(CheckStatusWrapper* status, const FB_I128* from, int scale, unsigned bufSize, char* buffer) + { + try + { + const Int128* i128 = (Int128*)from; + i128->toString(scale, bufSize, buffer); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + } + + void fromString(CheckStatusWrapper* status, int scale, const char* from, FB_I128* to) + { + try + { + Int128* i128 = (Int128*)to; + scale -= CVT_decompose(from, static_cast(strlen(from)), i128, errorFunction); + i128->setScale(scale); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + } + + static void errorFunction(const Arg::StatusVector& v) + { + v.raise(); + } +}; + +IInt128* UtilInterface::getInt128(CheckStatusWrapper* status) +{ + static IfaceInt128 ifaceInt128; + return &ifaceInt128; +} + unsigned UtilInterface::setOffsets(CheckStatusWrapper* status, IMessageMetadata* metadata, IOffsetsCallback* callback) {