| /********************************************************************** |
| // @@@ START COPYRIGHT @@@ |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| // @@@ END COPYRIGHT @@@ |
| **********************************************************************/ |
| /* -*-C++-*- |
| ***************************************************************************** |
| * |
| * File: BigNumHelper.cpp |
| * Description: Implementation of class BigNumHelper |
| * Created: 03/29/99 |
| * Language: C++ |
| * |
| * |
| * |
| * |
| ***************************************************************************** |
| */ |
| #include "Platform.h" |
| #include <math.h> |
| #define MathCeil(op,err) ceil(op) |
| |
| #include "str.h" |
| #include "BigNumHelper.h" |
| |
| static const unsigned short powersOfTen[] = {10, // 10^1 |
| 100, // 10^2 |
| 1000, // 10^3 |
| 10000}; // 10^4 |
| |
| static const unsigned short powersOfTenInBigNumForm[] = {0x0001, 0x0000, 0x0000, 0x0000, // 10^0 |
| 0x000A, 0x0000, 0x0000, 0x0000, // 10^1 |
| 0x0064, 0x0000, 0x0000, 0x0000, // 10^2 |
| 0x03E8, 0x0000, 0x0000, 0x0000, // 10^3 |
| 0x2710, 0x0000, 0x0000, 0x0000, // 10^4 |
| 0x86A0, 0x0001, 0x0000, 0x0000, // 10^5 |
| 0x4240, 0x000F, 0x0000, 0x0000, // 10^6 |
| 0x9680, 0x0098, 0x0000, 0x0000, // 10^7 |
| 0xE100, 0x05F5, 0x0000, 0x0000, // 10^8 |
| 0xCA00, 0x3B9A, 0x0000, 0x0000, // 10^9 |
| 0xE400, 0x540B, 0x0002, 0x0000, // 10^10 |
| 0xE800, 0x4876, 0x0017, 0x0000, // 10^11 |
| 0x1000, 0xD4A5, 0x00E8, 0x0000, // 10^12 |
| 0xA000, 0x4E72, 0x0918, 0x0000, // 10^13 |
| 0x4000, 0x107A, 0x5AF3, 0x0000, // 10^14 |
| 0x8000, 0xA4C6, 0x8D7E, 0x0003, // 10^15 |
| 0x0000, 0x6FC1, 0x86F2, 0x0023, // 10^16 |
| 0x0000, 0x5D8A, 0x4578, 0x0163, // 10^17 |
| 0x0000, 0xA764, 0xB6B3, 0x0DE0, // 10^18 |
| 0x0000, 0x89E8, 0x2304, 0x8AC7};// 10^19 |
| |
| |
| // The following method adds two Big Nums (without signs). |
| |
| short BigNumHelper::AddHelper(Lng32 dataLength, |
| char * leftData, |
| char * rightData, |
| char * resultData) |
| { |
| |
| // Recast from bytes to unsigned shorts. |
| Lng32 dataLengthInShorts = dataLength/2; |
| unsigned short * leftDataInShorts = (unsigned short *) leftData; |
| unsigned short * rightDataInShorts = (unsigned short *) rightData; |
| unsigned short * resultDataInShorts = (unsigned short *) resultData; |
| |
| #ifdef NA_LITTLE_ENDIAN |
| union { |
| ULng32 temp; |
| struct { |
| unsigned short remainder; |
| unsigned short carry; |
| } tempParts; |
| }; |
| #else |
| union { |
| ULng32 temp; |
| struct { |
| unsigned short carry; |
| unsigned short remainder; |
| } tempParts; |
| }; |
| #endif |
| |
| tempParts.carry = 0; |
| for (Lng32 j = 0; j < dataLengthInShorts; j++) |
| { |
| temp = ((ULng32) leftDataInShorts[j]) + rightDataInShorts[j] + tempParts.carry; |
| resultDataInShorts[j] = tempParts.remainder; |
| } |
| |
| return 0; |
| |
| } |
| |
| |
| // The following method subtracts one Big Num from another (without signs). |
| |
| short BigNumHelper::SubHelper(Lng32 dataLength, |
| char * leftData, |
| char * rightData, |
| char * resultData) |
| { |
| |
| // Recast from bytes to unsigned shorts. |
| Lng32 dataLengthInShorts = dataLength/2; |
| unsigned short * leftDataInShorts = (unsigned short *) leftData; |
| unsigned short * rightDataInShorts = (unsigned short *) rightData; |
| unsigned short * resultDataInShorts = (unsigned short *) resultData; |
| |
| // Check if left is smaller than right, and |
| // if so, switch left with right. |
| unsigned short * left = leftDataInShorts; |
| unsigned short * right = rightDataInShorts; |
| |
| Int32 neg = 0; |
| Lng32 j = 0; |
| for (j = 0; j < dataLengthInShorts; j++) { |
| if (rightDataInShorts[j] > leftDataInShorts[j]) |
| neg = -1; |
| else if (rightDataInShorts[j] < leftDataInShorts[j]) |
| neg = 0; |
| } |
| |
| if (neg == -1) |
| { |
| left = rightDataInShorts; |
| right = leftDataInShorts; |
| } |
| |
| short carry = 0; |
| Lng32 temp; |
| |
| #ifdef NA_LITTLE_ENDIAN |
| union { |
| ULng32 temp1; |
| struct { |
| unsigned short remainder; |
| unsigned short filler; |
| } tempParts; |
| }; |
| #else |
| union { |
| ULng32 temp1; |
| struct { |
| unsigned short filler; |
| unsigned short remainder; |
| } tempParts; |
| }; |
| #endif |
| |
| for (j = 0; j < dataLengthInShorts; j++) |
| { |
| temp = ((Lng32) left[j]) - right[j] + carry; |
| temp1 = temp + (Lng32) USHRT_MAX + 1; // Note that USHRT_MAX + 1 = 2^16. |
| resultDataInShorts[j] = tempParts.remainder; |
| carry = ( temp < 0 ? -1 : 0); |
| } |
| |
| return neg; |
| |
| } |
| |
| // The following method multiplies two Big Nums (without signs). |
| // The assumption is that the result is big enough to hold the |
| // product. |
| |
| short BigNumHelper::MulHelper(Lng32 resultLength, |
| Lng32 leftLength, |
| Lng32 rightLength, |
| char * leftData, |
| char * rightData, |
| char * resultData) |
| { |
| |
| // Recast from bytes to unsigned shorts. |
| unsigned short * leftDataInShorts = (unsigned short *) leftData; |
| unsigned short * rightDataInShorts = (unsigned short *) rightData; |
| unsigned short * resultDataInShorts = (unsigned short *) resultData; |
| |
| // Set result to zero. |
| for (Int32 k = 0; k < resultLength/2; k++) |
| resultDataInShorts[k] = 0; |
| |
| // Skip trailing zeros in the left and the right argument |
| // to shorten the nested loop that appears later. |
| Lng32 rightEndInShorts = rightLength/2 - 1; |
| while (!rightDataInShorts[rightEndInShorts] && rightEndInShorts >= 0) |
| rightEndInShorts--; |
| if (rightEndInShorts < 0) |
| return 0; |
| |
| Lng32 leftEndInShorts = leftLength/2 - 1; |
| while (!leftDataInShorts[leftEndInShorts] && leftEndInShorts >= 0) |
| leftEndInShorts--; |
| if (leftEndInShorts < 0) |
| return 0; |
| |
| // Int64 temp; |
| // unsigned long * remainder = (unsigned long *) &temp; |
| // unsigned long * carry = remainder + 1; |
| |
| #ifdef NA_LITTLE_ENDIAN |
| union { |
| ULng32 temp; |
| struct { |
| unsigned short remainder; |
| unsigned short carry; |
| } tempParts; |
| }; |
| #else |
| union { |
| ULng32 temp; |
| struct { |
| unsigned short carry; |
| unsigned short remainder; |
| } tempParts; |
| }; |
| #endif |
| |
| Lng32 pos; |
| for (Lng32 j = 0; j <= rightEndInShorts; j++) |
| { |
| tempParts.carry = 0; |
| pos = j; |
| for (Lng32 i = 0; i <= leftEndInShorts; i++, pos++) |
| { |
| temp = ((ULng32) leftDataInShorts[i]) * rightDataInShorts[j] + resultDataInShorts[pos] + tempParts.carry; |
| resultDataInShorts[pos] = tempParts.remainder; |
| } |
| |
| if (tempParts.carry) |
| resultDataInShorts[pos] = tempParts.carry; |
| } |
| |
| return 0; |
| |
| } |
| |
| // The following method divides one Big Num by another (both without signs), |
| // only if the divisor fits in an unsigned short. It returns 1 if there is |
| // a remainder, 0 if there is no remainder, and -1 if there is an error. |
| |
| short BigNumHelper::SimpleDivHelper(Lng32 dividendLength, |
| Lng32 divisorLength, |
| char * dividendData, |
| char * divisorData, |
| char * quotientData) |
| |
| { |
| // Recast from bytes to unsigned shorts. |
| Lng32 dividendLengthInShorts = dividendLength/2; |
| Lng32 divisorLengthInShorts = divisorLength/2; |
| unsigned short * dividendDataInShorts = (unsigned short *) dividendData; |
| unsigned short * divisorDataInShorts = (unsigned short *) divisorData; |
| unsigned short * quotientDataInShorts = (unsigned short *) quotientData; |
| |
| if (divisorLengthInShorts > 1) |
| return -1; // Error. |
| |
| unsigned short remainder = 0; |
| union { |
| ULng32 temp; |
| unsigned short temp1[2]; |
| }; |
| |
| for (Int32 j = dividendLengthInShorts - 1; j >= 0; j--) |
| { |
| |
| #ifdef NA_LITTLE_ENDIAN |
| temp1[0] = dividendDataInShorts[j]; |
| temp1[1] = remainder; |
| #else |
| temp1[1] = dividendDataInShorts[j]; |
| temp1[0] = remainder; |
| #endif |
| |
| quotientDataInShorts[j] = (unsigned short) (temp / divisorDataInShorts[0]); |
| remainder = (unsigned short ) (temp % divisorDataInShorts[0]); |
| } |
| |
| if (remainder != 0) |
| return 1; |
| else |
| return 0; |
| } |
| |
| // The following division algorithm is based on the one |
| // in Knuth's book on Seminumerical Algorithms, 2nd edition. |
| // Some of the steps in our implementation are more optimized |
| // to take advantage of base 2^16. |
| // |
| // The method divides one Big Num by another (both without signs). |
| // It returns 1 if there is a remainder, 0 if there is no remainder. |
| // (Note: there does not seem to be a need yet to actually calculate |
| // the remainder. If such a need arises in the future, the method |
| // should be changed accordingly.) |
| // |
| // The caller should pass tempData, which must point to memory |
| // (aligned on 2-byte boundary) of size (in bytes) : |
| // 3 * dividendLength + 6 |
| // to be used for temporary calculations. |
| |
| short BigNumHelper::DivHelper(Lng32 dividendLength, |
| Lng32 divisorLength, |
| char * dividendData, |
| char * divisorData, |
| char * quotientData, |
| char * tempData) |
| { |
| // Recast from bytes to unsigned shorts. |
| Lng32 dividendLengthInShorts = dividendLength/2; |
| Lng32 divisorLengthInShorts = divisorLength/2; |
| unsigned short * divisorDataInShorts = (unsigned short *) divisorData; |
| unsigned short * quotientDataInShorts = (unsigned short *) quotientData; |
| |
| char * tempDividendData = tempData; |
| unsigned short * tempDividendDataInShorts = (unsigned short *) tempDividendData; |
| unsigned short * tempDivisorDataInShorts = tempDividendDataInShorts + dividendLengthInShorts + 1; |
| unsigned short * tempDivisorData1InShorts = tempDivisorDataInShorts + divisorLengthInShorts + 1; |
| char * tempDivisorData = (char *) tempDivisorDataInShorts; |
| char * tempDivisorData1 = (char *) tempDivisorData1InShorts; |
| |
| // Begin normalizing step. |
| |
| #ifdef NA_LITTLE_ENDIAN |
| |
| if (divisorDataInShorts[divisorLengthInShorts - 1] <= UCHAR_MAX) { // Note that UCHAR_MAX = 0xFF |
| |
| // Multiply divisorData by 0x100 (= UCHAR_MAX + 1) and store in tempDivisorData. |
| // Note that the size of tempDivisorData is 2 bytes more than divisorData. |
| tempDivisorData[0] = 0; |
| Int32 i; |
| for (i = 0; i < divisorLength; i++) |
| tempDivisorData[i+1] = divisorData[i]; |
| tempDivisorData[divisorLength + 1] = 0; |
| |
| // Multiply dividendData by 0x100 and store in tempDividendData. |
| // Note that the size of tempDividendData is 2 bytes more than dividendData. |
| tempDividendData[0] = 0; |
| for (i = 0; i < dividendLength; i++) |
| tempDividendData[i+1] = dividendData[i]; |
| tempDividendData[dividendLength + 1] = 0; |
| |
| } |
| else { |
| Int32 i; |
| // Multiply divisorData by 1 and store in tempDivisorData. |
| for (i = 0; i < divisorLength; i++) |
| tempDivisorData[i] = divisorData[i]; |
| tempDivisorDataInShorts[divisorLengthInShorts] = 0; |
| |
| // Multiply dividendData by 1 and store in tempDividendData. |
| for (i = 0; i < dividendLength; i++) |
| tempDividendData[i] = dividendData[i]; |
| tempDividendDataInShorts[dividendLengthInShorts] = 0; |
| |
| } |
| |
| #else |
| |
| if (divisorDataInShorts[divisorLengthInShorts - 1] <= UCHAR_MAX) { |
| |
| Int32 i = 0; |
| |
| // Multiply divisorData by 0x100 and store in tempDivisorData. |
| tempDivisorData[1] = 0; |
| for (i = 0; i < divisorLengthInShorts; i++) { |
| tempDivisorData[2*i+3] = divisorData[2*i]; |
| tempDivisorData[2*i] = divisorData[2*i+1]; |
| } |
| tempDivisorData[divisorLength] = 0; |
| |
| // Multiply dividendData by 0x100 and store in tempDividendData. |
| tempDividendData[1] = 0; |
| for (i = 0; i < dividendLengthInShorts; i++) { |
| tempDividendData[2*i+3] = dividendData[2*i]; |
| tempDividendData[2*i] = dividendData[2*i+1]; |
| } |
| tempDividendData[dividendLength] = 0; |
| |
| } |
| |
| else { |
| |
| Int32 i = 0; |
| |
| // Multiply divisorData by 1 and store in tempDivisorData. |
| for (i = 0; i < divisorLength; i++) |
| tempDivisorData[i] = divisorData[i]; |
| tempDivisorDataInShorts[divisorLengthInShorts] = 0; |
| |
| // Multiply dividendData by 1 and store in tempDividendData. |
| for (i = 0; i < dividendLength; i++) |
| tempDividendData[i] = dividendData[i]; |
| tempDividendDataInShorts[dividendLengthInShorts] = 0; |
| |
| } |
| |
| #endif |
| // End normalizing step. |
| |
| // Set the quotient to zero. |
| Int32 j = 0; |
| for (j = 0; j < dividendLengthInShorts ; j++) |
| quotientDataInShorts[j] = 0; |
| |
| unsigned short q; |
| |
| union { |
| ULng32 temp1; |
| unsigned short temp2[2]; |
| }; |
| |
| union { |
| Int64 temp3; |
| unsigned short temp4[4]; |
| }; |
| |
| Int32 m = dividendLengthInShorts - divisorLengthInShorts; |
| Lng32 dividendPosInShorts = dividendLengthInShorts; |
| |
| // The main loop for division. |
| for (j = 0; j <= m; j++) { |
| |
| #ifdef NA_LITTLE_ENDIAN |
| temp4[3] = 0; |
| temp4[2] = tempDividendDataInShorts[dividendPosInShorts]; |
| temp4[1] = tempDividendDataInShorts[dividendPosInShorts - 1]; |
| temp4[0] = tempDividendDataInShorts[dividendPosInShorts - 2]; |
| temp2[1] = tempDivisorDataInShorts[divisorLengthInShorts - 1]; |
| temp2[0] = tempDivisorDataInShorts[divisorLengthInShorts - 2]; |
| #else |
| temp4[0] = 0; |
| temp4[1] = tempDividendDataInShorts[dividendPosInShorts]; |
| temp4[2] = tempDividendDataInShorts[dividendPosInShorts - 1]; |
| temp4[3] = tempDividendDataInShorts[dividendPosInShorts - 2]; |
| temp2[0] = tempDivisorDataInShorts[divisorLengthInShorts - 1]; |
| temp2[1] = tempDivisorDataInShorts[divisorLengthInShorts - 2]; |
| #endif |
| |
| q = (unsigned short) (temp3 / temp1); |
| |
| if (q == 0) |
| { |
| if (tempDividendDataInShorts[dividendLengthInShorts - j] == |
| tempDivisorDataInShorts[divisorLengthInShorts - 1]) |
| |
| q = USHRT_MAX; // Note that USHRT_MAX is the largest digit in base 2^16. |
| } |
| |
| BigNumHelper::MulHelper(divisorLength + 2, |
| divisorLength, |
| 2, |
| tempDivisorData, |
| (char *) &q, |
| tempDivisorData1); |
| |
| Int32 neg = BigNumHelper::SubHelper(divisorLength + 2, |
| (char *) &tempDividendDataInShorts[dividendPosInShorts - divisorLengthInShorts], |
| tempDivisorData1, |
| (char *) &tempDividendDataInShorts[dividendPosInShorts - divisorLengthInShorts]); |
| |
| if (neg) { |
| |
| // q is too large. |
| q--; |
| |
| // Add back by subtracting the result of the last subtraction (which is |
| // actually negative) from tempDividendData. We ignore the return code of |
| // SubHelper(), because we know the result is always positive. |
| BigNumHelper::SubHelper(divisorLength + 2, |
| tempDivisorData, |
| (char *) &tempDividendDataInShorts[dividendPosInShorts - divisorLengthInShorts], |
| (char *) &tempDividendDataInShorts[dividendPosInShorts - divisorLengthInShorts]); |
| } |
| |
| quotientDataInShorts[dividendPosInShorts - divisorLengthInShorts] = q; |
| |
| dividendPosInShorts--; |
| |
| } // for j |
| |
| // Check whether there was any remainder. |
| for (j = 0; j < divisorLengthInShorts; j++) { |
| if (tempDividendDataInShorts[j] != 0) |
| return 1; |
| } |
| return 0; |
| |
| } |
| |
| // The following method converts a given Big Num (without sign) into |
| // its equivalent BCD string representation (with the more significant decimal |
| // digits in the lower addresses). |
| |
| short BigNumHelper::ConvBigNumToBcdHelper(Lng32 sourceLength, |
| Lng32 targetLength, |
| char * sourceData, |
| char * targetData, |
| NAMemory * heap) |
| |
| { |
| // Recast from bytes to unsigned shorts. |
| Lng32 sourceLengthInShorts = sourceLength/2; |
| unsigned short * sourceDataInShorts = (unsigned short *) sourceData; |
| |
| unsigned short tempSourceDataInShortsBuf[128 / 2]; |
| unsigned short* tempSourceDataInShorts = tempSourceDataInShortsBuf; |
| |
| if (heap) |
| tempSourceDataInShorts = new (heap) unsigned short [sourceLength / 2]; |
| |
| Int32 i = 0; |
| for (i = 0; i < sourceLengthInShorts; i++) |
| tempSourceDataInShorts[i] = sourceDataInShorts[i]; |
| |
| // Initialize the BCD to zero. |
| for (i = 0; i < targetLength; i++) |
| targetData[i] = 0; |
| char * finalTargetData = targetData + targetLength - 1; |
| Lng32 finalTargetLength = 1; |
| |
| // Ignore trailing zeros in the Big Num. If all zeros, return. |
| Lng32 actualSourceLengthInShorts = sourceLengthInShorts; |
| while (!tempSourceDataInShorts[actualSourceLengthInShorts - 1] && actualSourceLengthInShorts > 0) |
| actualSourceLengthInShorts--; |
| if (!actualSourceLengthInShorts) { |
| if (heap) |
| NADELETEBASIC(tempSourceDataInShorts, (heap)); |
| return 0; |
| } |
| |
| union { |
| ULng32 temp; |
| unsigned short temp1[2]; |
| }; |
| |
| unsigned short remainder = 0; |
| |
| // Compute the final BCD as follows. Keep dividing the Big Num by 10^4 |
| // until the quotient becomes less than 10^4. At each iteration, compute |
| // the 4-digit BCD representation of the remainder, and append it to the |
| // left of the final BCD. For the final remainder, compute its BCD |
| // representation (which may be less than 4-digits) and append it to the |
| // left of the final BCD. |
| |
| finalTargetData++; |
| finalTargetLength = 0; |
| while ((actualSourceLengthInShorts != 1) || |
| (tempSourceDataInShorts[actualSourceLengthInShorts - 1] >= 10000)) { |
| |
| // Divide the Big Num by 10^4. It is more efficient to insert |
| // the division code than to call SimpleDivideHelper(); |
| Int32 j = 0; |
| for (j = actualSourceLengthInShorts - 1; j >= 0; j--) { |
| |
| #ifdef NA_LITTLE_ENDIAN |
| temp1[0] = tempSourceDataInShorts[j]; |
| temp1[1] = remainder; |
| #else |
| temp1[1] = tempSourceDataInShorts[j]; |
| temp1[0] = remainder; |
| #endif |
| |
| tempSourceDataInShorts[j] = (unsigned short) (temp / 10000); |
| remainder = (unsigned short) (temp % 10000); |
| } |
| if (!tempSourceDataInShorts[actualSourceLengthInShorts - 1]) |
| actualSourceLengthInShorts--; |
| |
| // Compute the BCD representation of the remainder and append to the |
| // left of the final BCD. |
| for (j = 0; j < 4; j++) { |
| if (finalTargetLength >= targetLength) { |
| if (heap) |
| NADELETEBASIC(tempSourceDataInShorts, (heap)); |
| return -1; |
| } |
| finalTargetData--; |
| finalTargetLength++; |
| finalTargetData[0] = remainder % 10; |
| remainder = remainder / 10; |
| } |
| } |
| |
| // Compute the BCD representation of the final remainder and append to the |
| // left of the final BCD. |
| remainder = tempSourceDataInShorts[0]; |
| while (remainder) { |
| if (finalTargetLength >= targetLength) { |
| if (heap) |
| NADELETEBASIC(tempSourceDataInShorts, (heap)); |
| return -1; |
| } |
| finalTargetData--; |
| finalTargetLength++; |
| finalTargetData[0] = remainder % 10; |
| remainder = remainder / 10; |
| } |
| |
| if (heap) |
| NADELETEBASIC(tempSourceDataInShorts, (heap)); |
| |
| return 0; |
| } |
| |
| // The following method converts a given BCD string representation (without sign, |
| // and with the more significant decimal digits in the lower addresses) |
| // into its equivalent Big Num representation. |
| |
| short BigNumHelper::ConvBcdToBigNumHelper(Lng32 sourceLength, |
| Lng32 targetLength, |
| char * sourceData, |
| char * targetData) |
| |
| |
| { |
| // Recast from bytes to unsigned shorts. |
| Lng32 targetLengthInShorts = targetLength/2; |
| unsigned short * targetDataInShorts = (unsigned short *) targetData; |
| |
| // Initialize the Big Num to zero. |
| Int32 i = 0; |
| for (i = 0; i < targetLengthInShorts; i++) |
| targetDataInShorts[i] = 0; |
| Lng32 finalTargetLengthInShorts = 1; |
| |
| // Ignore leading zeros in BCD. If all zeros, return. |
| Lng32 zeros = 0; |
| while ((zeros < sourceLength) && !sourceData[zeros]) |
| zeros++; |
| if (zeros == sourceLength) |
| return 0; |
| |
| Int32 actualSourceLength = sourceLength - zeros; |
| char * actualSourceData = sourceData + zeros; |
| |
| |
| #ifdef NA_LITTLE_ENDIAN |
| union { |
| ULng32 temp; |
| struct { |
| unsigned short remainder; |
| unsigned short carry; |
| } tempParts; |
| }; |
| #else |
| union { |
| ULng32 temp; |
| struct { |
| unsigned short carry; |
| unsigned short remainder; |
| } tempParts; |
| }; |
| #endif |
| |
| // Compute the Big Num as follows. First, chunk up the BCD |
| // string into chunks of 4-digit unsigned short numbers. Iterate |
| // over these chunks, and at each iteration, multiply the current |
| // Big Num by 10^4 and then add the numeric value of this chunk |
| // to it. |
| for (i = 0; i < actualSourceLength; i += 4) { |
| |
| // Compute the numeric value of the next 4-digit chunk. |
| unsigned short temp1 = 0; |
| Int32 j = 0; |
| while ((j < 4) && (i+j < actualSourceLength)) { |
| temp1 = temp1*10 + actualSourceData[i+j]; |
| j++; |
| } |
| unsigned short power = powersOfTen[j - 1]; |
| |
| // Multiply the previous Big Num by 10^4 and add the value |
| // of the current chunk to it. It is more efficient to insert the |
| // multiplication and addition code here than to call the Helper() |
| // methods. |
| temp = ((ULng32) targetDataInShorts[0]) * power + temp1; |
| targetDataInShorts[0] = tempParts.remainder; |
| for (j = 1; j < finalTargetLengthInShorts; j++) { |
| temp = ((ULng32) targetDataInShorts[j]) * power + tempParts.carry; |
| targetDataInShorts[j] = tempParts.remainder; |
| } |
| if (tempParts.carry) { |
| if (finalTargetLengthInShorts >= targetLengthInShorts) |
| return -1; |
| finalTargetLengthInShorts++; |
| targetDataInShorts[finalTargetLengthInShorts - 1] = tempParts.carry; |
| } |
| } |
| |
| return 0; |
| |
| } |
| |
| // The following method converts a given Big Num (with sign) into |
| // its equivalent BCD string representation (with the more significant |
| // decimal digits in the lower addresses). |
| |
| short BigNumHelper::ConvBigNumWithSignToBcdHelper(Lng32 sourceLength, |
| Lng32 targetLength, |
| char * sourceData, |
| char * targetData, |
| NAMemory * heap) |
| { |
| char sign = BIGN_GET_SIGN(sourceData, sourceLength); |
| |
| // Set up sign. |
| targetData[0] = (sign) ? '-' : '+'; |
| |
| // Temporarily clear sign |
| BIGN_CLR_SIGN(sourceData, sourceLength); |
| |
| short returnValue = BigNumHelper::ConvBigNumToBcdHelper(sourceLength, |
| targetLength - 1, |
| sourceData, |
| targetData + 1, |
| heap); |
| |
| // Restore sign |
| if (sign) |
| BIGN_SET_SIGN(sourceData, sourceLength); |
| |
| return returnValue; |
| } |
| |
| // The following method converts a given BCD string representation |
| // (with sign, and with the more significant decimal digits in the lower |
| // addresses) into its equivalent Big Num representation. |
| |
| short BigNumHelper::ConvBcdToBigNumWithSignHelper(Lng32 sourceLength, |
| Lng32 targetLength, |
| char * sourceData, |
| char * targetData) |
| { |
| short returnValue = BigNumHelper::ConvBcdToBigNumHelper(sourceLength - 1, |
| targetLength, |
| sourceData + 1, |
| targetData); |
| |
| // Set up sign after magnitude set. TargetData already cleared. |
| if (sourceData[0] == '-') |
| BIGN_SET_SIGN(targetData, targetLength); |
| |
| return returnValue; |
| } |
| |
| |
| // The following method converts a given Big Num (without sign) into |
| // its equivalent ASCII string representation (with the more significant |
| // decimal digits in the lower addresses). |
| |
| short BigNumHelper::ConvBigNumToAsciiHelper(Lng32 sourceLength, |
| Lng32 targetLength, |
| char * sourceData, |
| char * targetData, |
| NAMemory * heap) |
| { |
| |
| // Convert to BCD representation (without sign). |
| short returnValue = BigNumHelper::ConvBigNumToBcdHelper(sourceLength, |
| targetLength, |
| sourceData, |
| targetData, |
| heap); |
| // Convert from BCD to ASCII. |
| for (Int32 i = 0; i < targetLength; i++) |
| targetData[i] += '0'; |
| |
| return returnValue; |
| |
| } |
| |
| // The following method converts a given ASCII string representation |
| // (without sign, and with the more significant decimal digits in the lower |
| // addresses) into its equivalent Big Num representation. |
| |
| short BigNumHelper::ConvAsciiToBigNumHelper(Lng32 sourceLength, |
| Lng32 targetLength, |
| char * sourceData, |
| char * targetData) |
| { |
| // Temporarily convert source from ASCII to BCD. |
| Int32 i = 0; |
| for (i = 0; i < sourceLength; i++) |
| sourceData[i] -= '0'; |
| |
| short returnValue = BigNumHelper::ConvBcdToBigNumHelper(sourceLength, |
| targetLength, |
| sourceData, |
| targetData); |
| // Restore source to ASCII. |
| for (i = 0; i < sourceLength; i++) |
| sourceData[i] += '0'; |
| |
| return returnValue; |
| |
| } |
| |
| // The following method converts a given Big Num (with sign) into |
| // its equivalent ASCII string representation (with the more significant |
| // decimal digits in the lower addresses). |
| |
| short BigNumHelper::ConvBigNumWithSignToAsciiHelper(Lng32 sourceLength, |
| Lng32 targetLength, |
| char * sourceData, |
| char * targetData, |
| NAMemory * heap) |
| { |
| char sign = BIGN_GET_SIGN(sourceData, sourceLength); |
| |
| // Set up sign. |
| targetData[0] = (sign) ? '-' : '+'; |
| |
| // Temporarily clear sign |
| BIGN_CLR_SIGN(sourceData, sourceLength); |
| |
| short returnValue = BigNumHelper::ConvBigNumToAsciiHelper(sourceLength, |
| targetLength - 1, |
| sourceData, |
| targetData + 1, |
| heap); |
| |
| // Restore sign |
| if (sign) |
| BIGN_SET_SIGN(sourceData, sourceLength); |
| |
| return returnValue; |
| } |
| |
| // The following method converts a given ASCII string representation |
| // (with sign, and with the more significant decimal digits in the lower |
| // addresses) into its equivalent Big Num representation. |
| |
| short BigNumHelper::ConvAsciiToBigNumWithSignHelper(Lng32 sourceLength, |
| Lng32 targetLength, |
| char * sourceData, |
| char * targetData) |
| { |
| short returnValue = BigNumHelper::ConvAsciiToBigNumHelper(sourceLength - 1, |
| targetLength, |
| sourceData + 1, |
| targetData); |
| |
| // Set up sign. |
| if (sourceData[0] == '-') |
| BIGN_SET_SIGN(targetData, targetLength); |
| |
| return returnValue; |
| } |
| |
| // Given a desired precision of a Big Num, the following method calculates |
| // the required storage length (including the sign). We assume that the |
| // precision is > 0. |
| |
| Lng32 BigNumHelper::ConvPrecisionToStorageLengthHelper(Lng32 precision) |
| { |
| // The storage length is always a multiple of 2 bytes, and at minimum, 8 bytes |
| // long. 1 bit must be available for the sign. Thus we use the formula: |
| // |
| // storage length = ceil((precision*log2(10) + 1)/16) |
| // |
| short err = 0; |
| |
| // Change precision to be a minimum of 18 (i.e. 8 bytes) |
| if (precision < 18) |
| precision = 18; |
| |
| Lng32 stLen = (2*(Lng32)(MathCeil((precision*0.208) + 0.062, err))); |
| |
| if (err) |
| return -1; // is -1 error return code from here??? |
| |
| return stLen; |
| } |
| |
| |
| // The following method converts an integer, 10^exponent, to a Big Num |
| // representation (without sign). The given exponent should be >= 0. The |
| // given targetData should have a length which is a multiple of 8. |
| |
| short BigNumHelper::ConvPowersOfTenToBigNumHelper(Lng32 exponent, |
| Lng32 targetLength, |
| Lng32 * finalTargetLength, |
| char * targetData) |
| |
| { |
| // If exponent is small enough, copy Big Num from precomputed table. |
| if (exponent < sizeof(powersOfTenInBigNumForm)/8) { |
| for (Int32 k = 0; k < 8 ; k++) |
| targetData[k] = ((char *) (powersOfTenInBigNumForm + exponent*4))[k]; |
| *finalTargetLength = 8; |
| return 0; |
| } |
| |
| // Otherwise, we have to actually compute 10^exponent iteratively. |
| // Recast from bytes to unsigned shorts. |
| unsigned short * targetDataInShorts = (unsigned short *) targetData; |
| *finalTargetLength = BigNumHelper::ConvPrecisionToStorageLengthHelper(exponent); |
| |
| Lng32 diffExponent = exponent - sizeof(powersOfTenInBigNumForm)/8 + 1; |
| |
| #ifdef NA_LITTLE_ENDIAN |
| union { |
| ULng32 temp; |
| struct { |
| unsigned short remainder; |
| unsigned short carry; |
| } tempParts; |
| }; |
| #else |
| union { |
| ULng32 temp; |
| struct { |
| unsigned short carry; |
| unsigned short remainder; |
| } tempParts; |
| }; |
| #endif |
| |
| // Initialize the Big Num to 10^(sizeof(powersOfTenInBigNumForm)/8 - 1) |
| Int32 k = 0; |
| for (k = 0; k < 8; k++) |
| targetData[k] = ((char *) (powersOfTenInBigNumForm + sizeof(powersOfTenInBigNumForm)/2 - 4))[k]; |
| Lng32 currentTargetLengthInShorts = 4; |
| |
| // Multiply the Big Num repeatedly by 10^4 (excepting the last |
| // multiplication, which is by 10^(diffExponent%4). It is more |
| // efficient to insert the multiplication code here than to |
| // call the mulHelper() method. |
| |
| for (Int32 i = 0; i < diffExponent; i += 4) { |
| |
| unsigned short power; |
| if (i + 4 <= diffExponent) |
| power = 10000; |
| else |
| power = powersOfTen[(diffExponent % 4) - 1]; |
| |
| temp = ((ULng32) targetDataInShorts[0]) * power; |
| targetDataInShorts[0] = tempParts.remainder; |
| Int32 j = 1; |
| for (j = 1; j < currentTargetLengthInShorts; j++) { |
| temp = ((ULng32) targetDataInShorts[j]) * power + tempParts.carry; |
| targetDataInShorts[j] = tempParts.remainder; |
| } |
| if (tempParts.carry) { |
| currentTargetLengthInShorts++; |
| targetDataInShorts[currentTargetLengthInShorts - 1] = tempParts.carry; |
| } |
| |
| } |
| |
| // Fill up the trailing part of the target with zeros. |
| for (k = 2*currentTargetLengthInShorts; k < *finalTargetLength; k++) |
| targetData[k] = 0; |
| |
| return 0; |
| |
| } |
| |
| // The following converts an Int64 to a Big Num (with sign). |
| |
| short BigNumHelper::ConvInt64ToBigNumWithSignHelper(Int32 targetLength, |
| Int64 sourceData, |
| char * targetData, |
| NABoolean isUnsigned) |
| |
| { |
| Int32 tgtLength16 = targetLength >> 1; |
| UInt16* tgt = (UInt16*)targetData; |
| Int64* tgt64 = (Int64*)targetData; |
| NABoolean isNeg = FALSE; |
| |
| |
| // Initialize magnitude of target to zero. |
| for (Int32 k = 0; k < tgtLength16; k++) |
| tgt[k] = 0; |
| |
| if ((NOT isUnsigned) && (sourceData < 0)) { |
| sourceData = -sourceData; |
| isNeg = TRUE; |
| } |
| |
| *tgt64 = sourceData; |
| |
| // If little endian, do nothing, target is already in correct form. |
| #ifndef NA_LITTLE_ENDIAN |
| UInt16 temp1 = tgt[0]; |
| UInt16 temp2 = tgt[1]; |
| tgt[0] = tgt[3]; |
| tgt[1] = tgt[2]; |
| tgt[2] = temp2; |
| tgt[3] = temp1; |
| #endif |
| |
| if (isNeg) |
| BIGN_SET_SIGN(tgt, targetLength); |
| |
| return 0; |
| |
| } |
| |
| // The following converts a Big Num (with sign) into an Int64. |
| |
| short BigNumHelper::ConvBigNumWithSignToInt64Helper(Lng32 sourceLength, |
| char * sourceData, |
| void * targetDataPtr, |
| NABoolean isUnsigned) |
| |
| { |
| UInt64 *uTargetData = (UInt64*)targetDataPtr; |
| Int64 *targetData = (Int64*)targetDataPtr; |
| |
| // Recast from bytes to unsigned shorts. |
| unsigned short * sourceDataInShorts = (unsigned short *) sourceData; |
| Lng32 sourceLengthInShorts = sourceLength/2; |
| char srcSign = BIGN_GET_SIGN(sourceData, sourceLength); |
| |
| // Clear source sign temporarily |
| BIGN_CLR_SIGN(sourceData, sourceLength); |
| |
| // Remove trailing zeros in source. Note that we want a length of 4 unsigned shorts. |
| while (sourceDataInShorts[sourceLengthInShorts - 1] == 0 && sourceLengthInShorts > 4) |
| sourceLengthInShorts--; |
| |
| // Check for overflow. Source cannot have more than 4 unsigned shorts. |
| if (sourceLengthInShorts > 4) { |
| // Restore sign in source |
| if (srcSign) |
| BIGN_SET_SIGN(sourceData, sourceLength); |
| return -1; |
| } |
| |
| // Copy the magnitude of source to target. |
| // TODO: unaligned store here |
| *targetData = *((Int64 *) sourceData); |
| |
| // Restore sign in source |
| if (srcSign) |
| BIGN_SET_SIGN(sourceData, sourceLength); |
| |
| #ifdef NA_LITTLE_ENDIAN |
| // Do nothing, target already in correct format. |
| #else |
| // Reverse the shorts in the target. |
| unsigned short * targetDataInShorts = (unsigned short *) targetData; |
| |
| unsigned short temp = targetDataInShorts[0]; |
| targetDataInShorts[0] = targetDataInShorts[3]; |
| targetDataInShorts[3] = temp; |
| temp = targetDataInShorts[1]; |
| targetDataInShorts[1] = targetDataInShorts[2]; |
| targetDataInShorts[2] = temp; |
| #endif |
| |
| // We now check the sign of the source. Unfortunately there are complications. |
| // The target, an Int64, ranges between 2^63-1 and -2^63, which is asymmetric. |
| // We have to make sure that the source did not contain a Big Num outside this range. |
| |
| if (srcSign == 0) { // source is positive. |
| if (isUnsigned) |
| return 0; // all values are ok |
| |
| else if (*targetData >= 0) // target magnitude is between 0 and 2^63-1. |
| return 0; |
| else // target magnitude is beyond 2^63-1. |
| return -1; |
| } |
| else { // source is negative. |
| if (isUnsigned) |
| return -1; // error |
| else if (*targetData >= 0) { // target magnitude is between 0 and 2^63-1. |
| *targetData = -*targetData; |
| return 0; |
| } |
| else if (*targetData == LLONG_MIN) // target magnitude is 2^63. We do not even |
| // have to negate the target, because the bit |
| // representations of 2^63 and -2^63 (the latter |
| // in 2's-complement) are the same. |
| return 0; |
| else // target magnitude is beyond 2^63 |
| return -1; |
| } |
| |
| } |
| |
| |
| // The following converts a BIGNUM (with sign) into Int64 and scale it, |
| // returning information about the conversion to the caller (e.g. |
| // truncations, overflows). |
| // |
| // The return value can have 5 possible values: |
| // 0: the conversion was ok |
| // 1: the result was rounded up to LLONG_MIN |
| // 2: the result was rounded down to LLONG_MAX |
| // 3: the result was rounded up |
| // 4: the result was rounded down |
| |
| short BigNumHelper::ConvBigNumWithSignToInt64AndScaleHelper(Lng32 sourceLength, |
| char * sourceData, |
| Int64 * targetData, |
| Lng32 exponent, |
| NAMemory * heap) |
| { |
| |
| Lng32 sourceLengthInShorts = sourceLength/2; |
| unsigned short * targetDataInShorts = (unsigned short *) targetData; |
| |
| Lng32 absExponent = (exponent >= 0 ? exponent : -exponent); |
| |
| char srcSign = BIGN_GET_SIGN(sourceData, sourceLength); |
| |
| // Clear source sign temporarily |
| BIGN_CLR_SIGN(sourceData, sourceLength); |
| |
| // Allocate temp space for various calculations: |
| |
| // Allocate space for the scale factor, which is a Big Num (without sign) with value 10^(|exponent|). |
| Lng32 crudeScaleFactorLength = BigNumHelper::ConvPrecisionToStorageLengthHelper(absExponent + 1); |
| unsigned short * scaleFactorInShorts = new (heap) unsigned short [crudeScaleFactorLength/2]; |
| char * scaleFactor = (char *) scaleFactorInShorts; |
| |
| // Allocate space for temporary Big Num (without sign) to store source after scaling. |
| unsigned short * sourceDataAfterScalingInShorts = new (heap) unsigned short [(crudeScaleFactorLength + sourceLength)/2]; |
| char * sourceDataAfterScaling = (char *) sourceDataAfterScalingInShorts; |
| Lng32 sourceLengthAfterScalingInShorts; |
| |
| // Allocate temp space for the division routine; see DivHelper for details. |
| unsigned short * tempDataInShorts = new (heap) unsigned short [3 * (sourceLength) / 2 + 3]; |
| char * tempData = (char *) tempDataInShorts; |
| |
| Lng32 scaleFactorLength; |
| BigNumHelper::ConvPowersOfTenToBigNumHelper(absExponent, |
| crudeScaleFactorLength, |
| &scaleFactorLength, |
| scaleFactor); |
| |
| // Since the above function returns a Big Num with length a multiple of 8, there may be |
| // trailing zeros. Adjust the length to ignore trailing zeros. |
| Lng32 scaleFactorLengthInShorts = scaleFactorLength/2; |
| while (scaleFactorInShorts[scaleFactorLengthInShorts - 1] == 0 && scaleFactorLengthInShorts > 1) |
| scaleFactorLengthInShorts--; |
| scaleFactorLength = 2*scaleFactorLengthInShorts; |
| |
| short anyTruncation = 0; |
| |
| if (exponent < 0) { |
| // If exponent is negative, we need to scale up. Thus divide source by scaleFactor. |
| // If there is a remainder, this signifies that some truncation occurred. |
| |
| if (scaleFactorLengthInShorts > sourceLengthInShorts) { |
| // I.e. the scaleFactor is longer than the magnitude of the source, |
| // thus everything got truncated! Set source after scaling to zero. |
| for (Int32 k = 0; k < (crudeScaleFactorLength + sourceLength); k++) |
| sourceDataAfterScaling[k] = 0; |
| sourceLengthAfterScalingInShorts = 4; |
| anyTruncation = 1; |
| } |
| else if (scaleFactorLengthInShorts == 1) { |
| anyTruncation = BigNumHelper::SimpleDivHelper(sourceLength, |
| 2, |
| sourceData, |
| scaleFactor, |
| sourceDataAfterScaling); |
| sourceLengthAfterScalingInShorts = sourceLengthInShorts; |
| } |
| else { |
| anyTruncation = BigNumHelper::DivHelper(sourceLength, |
| scaleFactorLength, |
| sourceData, |
| scaleFactor, |
| sourceDataAfterScaling, |
| tempData); |
| sourceLengthAfterScalingInShorts = sourceLengthInShorts; |
| } |
| } |
| |
| else { |
| // If exponent is positive, we need to scale down, so multiply source by scaleFactor. |
| BigNumHelper::MulHelper(sourceLength + scaleFactorLength, |
| sourceLength, // ignore the sign byte |
| scaleFactorLength, |
| sourceData, |
| scaleFactor, |
| sourceDataAfterScaling); |
| sourceLengthAfterScalingInShorts = sourceLengthInShorts + scaleFactorLengthInShorts; |
| |
| } |
| |
| // Adjust the length of source after scaling to ignore trailing zeros (we do not have to |
| // go below a length of 4). |
| while (sourceDataAfterScalingInShorts[sourceLengthAfterScalingInShorts - 1] == 0 |
| && sourceLengthAfterScalingInShorts > 4) |
| sourceLengthAfterScalingInShorts--; |
| |
| |
| short retValue = 0; |
| |
| // Check for overflow. After scaling, the magnitude of source cannot have more |
| // than 4 unsigned shorts. |
| if (sourceLengthAfterScalingInShorts > 4) { |
| if (srcSign == 0) { // source is positive. |
| *targetData = LLONG_MAX; |
| retValue = 2; // the result was rounded down to LLONG_MAX. |
| } |
| else { |
| *targetData = LLONG_MIN; |
| retValue = 1; // the result was rounded up to LLONG_MIN. |
| } |
| } |
| |
| else { |
| |
| // Copy the magnitude of source to target. |
| *targetData = *((Int64 *) sourceDataAfterScaling); |
| |
| #ifdef NA_LITTLE_ENDIAN |
| // Do nothing, target already in correct format. |
| #else |
| // Reverse the shorts in the target. |
| unsigned short temp = targetDataInShorts[0]; |
| targetDataInShorts[0] = targetDataInShorts[3]; |
| targetDataInShorts[3] = temp; |
| temp = targetDataInShorts[1]; |
| targetDataInShorts[1] = targetDataInShorts[2]; |
| targetDataInShorts[2] = temp; |
| #endif |
| |
| // The target, an Int64, should range between 2^63-1 and -2^63, which is asymmetric. |
| // We have to make sure that the source did not contain a Big Num outside this range. |
| |
| if (srcSign == 0) { // source is positive. |
| |
| if (*targetData < 0) { // target magnitude is beyond 2^63-1. |
| *targetData = LLONG_MAX; |
| retValue = 2; // the result was rounded down to LLONG_MAX. |
| } |
| |
| } |
| else { // source is negative. |
| |
| if (*targetData < 0 && *targetData != LLONG_MIN) { // target magnitude is beyond 2^63 |
| *targetData = LLONG_MIN; |
| retValue = 1; // the result was rounded up to LLONG_MIN. |
| } |
| else if (*targetData != LLONG_MIN) { |
| // target magnitude is between 0 and 2^63. |
| // The value LLONG_MIN is already in correct format and doesn't |
| // need to be negated. |
| *targetData = -*targetData; |
| } |
| } |
| |
| if (retValue == 0 && anyTruncation) { |
| if (srcSign == 0) |
| retValue = 4; // the result was rounded down. |
| else |
| retValue = 3; // the result was rounded up; |
| } |
| |
| } |
| |
| NADELETEBASIC(scaleFactorInShorts, (heap)); |
| NADELETEBASIC(sourceDataAfterScalingInShorts, (heap)); |
| NADELETEBASIC(tempDataInShorts, (heap)); |
| |
| // Restore sign in source |
| if (srcSign) |
| BIGN_SET_SIGN(sourceData, sourceLength); |
| |
| return retValue; |
| |
| } |
| |
| // The following converts a Big Num to a Big Num. |
| |
| short BigNumHelper::ConvBigNumWithSignToBigNumWithSignHelper(Lng32 sourceLength, |
| Lng32 targetLength, |
| char * sourceData, |
| char * targetData) |
| { |
| // Recast char strings as arrays of unsigned shorts |
| unsigned short * sourceDataInShorts = (unsigned short *) sourceData; |
| unsigned short * targetDataInShorts = (unsigned short *) targetData; |
| Lng32 sourceLengthInShorts = sourceLength/2; // length in number of shorts. |
| |
| char srcSign = BIGN_GET_SIGN(sourceData, sourceLength); |
| |
| // Clear sign bit |
| BIGN_CLR_SIGN(sourceData, sourceLength); |
| |
| // Ignore trailing zeros in source. Minimum number of shorts in source is 2. |
| while ((sourceDataInShorts[sourceLengthInShorts - 1] == 0) && (sourceLengthInShorts > 2)) |
| sourceLengthInShorts--; |
| |
| // Return error if overflow |
| if (sourceLengthInShorts > targetLength/2) { |
| // Reset sign bit of source |
| if (srcSign) |
| BIGN_SET_SIGN(sourceData, sourceLength); |
| return -1; |
| } |
| |
| // Initialize magnitude of target to zeros. |
| Int32 k = 0; |
| for (k = 0; k < targetLength; k++) |
| targetData[k] = 0; |
| |
| // Copy source to target. |
| for (k = 0; k < sourceLengthInShorts; k++) |
| targetDataInShorts[k] = sourceDataInShorts[k]; |
| |
| // Restore sign in source and set sign in target |
| if (srcSign) { |
| BIGN_SET_SIGN(sourceData, sourceLength); |
| BIGN_SET_SIGN(targetData, targetLength); |
| } |
| |
| return 0; |
| } |
| |
| |
| |