Merge pull request #7440 from boyu0/add_utils_atof

Add utils::atof(), fix bug: particle not shown on android.
This commit is contained in:
minggo 2014-07-17 09:59:15 +08:00
commit a653d0eb28
20 changed files with 123 additions and 81 deletions

View File

@ -28,6 +28,7 @@
#include "CCObjLoader.h"
#include "platform/CCFileUtils.h"
#include "base/ccUtils.h"
NS_CC_BEGIN
@ -102,7 +103,7 @@ static inline int parseInt(const char*& token)
static inline float parseFloat(const char*& token)
{
token += strspn(token, " \t");
float f = (float)atof(token);
float f = (float)utils::atof(token);
token += strcspn(token, " \t\r");
return f;
}

View File

@ -63,6 +63,7 @@
#include "renderer/CCTextureCache.h"
#include "CCGLView.h"
#include "base/base64.h"
#include "base/ccUtils.h"
NS_CC_BEGIN
extern const char* cocos2dVersion(void);
@ -660,8 +661,8 @@ void Console::commandTouch(int fd, const std::string& args)
if((argv.size() == 3) && (isFloat(argv[1]) && isFloat(argv[2])))
{
float x = std::atof(argv[1].c_str());
float y = std::atof(argv[2].c_str());
float x = utils::atof(argv[1].c_str());
float y = utils::atof(argv[2].c_str());
srand ((unsigned)time(nullptr));
_touchId = rand();
@ -686,10 +687,10 @@ void Console::commandTouch(int fd, const std::string& args)
&& (isFloat(argv[3])) && (isFloat(argv[4])))
{
float x1 = std::atof(argv[1].c_str());
float y1 = std::atof(argv[2].c_str());
float x2 = std::atof(argv[3].c_str());
float y2 = std::atof(argv[4].c_str());
float x1 = utils::atof(argv[1].c_str());
float y1 = utils::atof(argv[2].c_str());
float x2 = utils::atof(argv[3].c_str());
float y2 = utils::atof(argv[4].c_str());
srand ((unsigned)time(nullptr));
_touchId = rand();

View File

@ -28,6 +28,8 @@ THE SOFTWARE.
#include <string.h>
#include <stdlib.h>
#include "base/ccUtils.h"
using namespace std;
NS_CC_BEGIN
@ -133,10 +135,10 @@ Rect RectFromString(const std::string& str)
strArray sizeInfo;
CC_BREAK_IF(!splitWithForm(sizeStr.c_str(), sizeInfo));
float x = (float) atof(pointInfo[0].c_str());
float y = (float) atof(pointInfo[1].c_str());
float width = (float) atof(sizeInfo[0].c_str());
float height = (float) atof(sizeInfo[1].c_str());
float x = (float) utils::atof(pointInfo[0].c_str());
float y = (float) utils::atof(pointInfo[1].c_str());
float width = (float) utils::atof(sizeInfo[0].c_str());
float height = (float) utils::atof(sizeInfo[1].c_str());
result = Rect(x, y, width, height);
} while (0);
@ -153,8 +155,8 @@ Vec2 PointFromString(const std::string& str)
strArray strs;
CC_BREAK_IF(!splitWithForm(str, strs));
float x = (float) atof(strs[0].c_str());
float y = (float) atof(strs[1].c_str());
float x = (float) utils::atof(strs[0].c_str());
float y = (float) utils::atof(strs[1].c_str());
ret = Vec2(x, y);
} while (0);
@ -171,8 +173,8 @@ Size SizeFromString(const std::string& pszContent)
strArray strs;
CC_BREAK_IF(!splitWithForm(pszContent, strs));
float width = (float) atof(strs[0].c_str());
float height = (float) atof(strs[1].c_str());
float width = (float) utils::atof(strs[0].c_str());
float height = (float) utils::atof(strs[1].c_str());
ret = Size(width, height);
} while (0);

View File

@ -27,6 +27,7 @@ THE SOFTWARE.
#include "platform/CCFileUtils.h"
#include "tinyxml2.h"
#include "base/base64.h"
#include "base/ccUtils.h"
#if (CC_TARGET_PLATFORM != CC_PLATFORM_IOS && CC_TARGET_PLATFORM != CC_PLATFORM_MAC && CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID)
@ -250,7 +251,7 @@ double UserDefault::getDoubleForKey(const char* pKey, double defaultValue)
if (value)
{
ret = atof(value);
ret = utils::atof(value);
}
if (doc) delete doc;

View File

@ -24,6 +24,7 @@ THE SOFTWARE.
****************************************************************************/
#include "base/CCUserDefault.h"
#include "base/CCPlatformConfig.h"
#include "base/ccUtils.h"
#include "platform/CCCommon.h"
#include "base/base64.h"
@ -243,7 +244,7 @@ float UserDefault::getFloatForKey(const char* pKey, float defaultValue)
{
if (node->FirstChild())
{
float ret = atof((const char*)node->FirstChild()->Value());
float ret = utils::atof((const char*)node->FirstChild()->Value());
// set value in NSUserDefaults
setFloatForKey(pKey, ret);
@ -279,7 +280,7 @@ double UserDefault::getDoubleForKey(const char* pKey, double defaultValue)
{
if (node->FirstChild())
{
double ret = atof((const char*)node->FirstChild()->Value());
double ret = utils::atof((const char*)node->FirstChild()->Value());
// set value in NSUserDefaults
setDoubleForKey(pKey, ret);

View File

@ -25,6 +25,7 @@
#include "base/CCValue.h"
#include <sstream>
#include <iomanip>
#include "base/ccUtils.h"
NS_CC_BEGIN
@ -504,7 +505,7 @@ float Value::asFloat() const
if (_type == Type::STRING)
{
return atof(_field.strVal->c_str());
return utils::atof(_field.strVal->c_str());
}
if (_type == Type::INTEGER)
@ -540,7 +541,7 @@ double Value::asDouble() const
if (_type == Type::STRING)
{
return static_cast<double>(atof(_field.strVal->c_str()));
return static_cast<double>(utils::atof(_field.strVal->c_str()));
}
if (_type == Type::INTEGER)

View File

@ -24,6 +24,9 @@ THE SOFTWARE.
****************************************************************************/
#include "base/ccUtils.h"
#include <stdlib.h>
#include "base/CCDirector.h"
#include "renderer/CCCustomCommand.h"
#include "renderer/CCRenderer.h"
@ -160,6 +163,27 @@ std::vector<Node*> findChildren(const Node &node, const std::string &name)
return vec;
}
#define MAX_ITOA_BUFFER_SIZE 256
double atof(const char* str)
{
if (str == nullptr)
{
return 0.0;
}
char buf[MAX_ITOA_BUFFER_SIZE];
strncpy(buf, str, MAX_ITOA_BUFFER_SIZE);
// strip string, only remain 7 numbers after '.'
char* dot = strchr(buf, '.');
if (dot != nullptr && dot - buf + 8 < MAX_ITOA_BUFFER_SIZE)
{
dot[8] = '\0';
}
return ::atof(buf);
}
}

View File

@ -73,6 +73,11 @@ namespace utils
* @since v3.2
*/
std::vector<Node*> findChildren(const Node &node, const std::string &name);
/** Same to ::atof, but strip the string, remain 7 numbers after '.' before call atof。
* Why we need this? Because in android c++_static, atof ( and std::atof ) is unsupported for numbers have long decimal part and contain several numbers can approximate to 1 like 90.099998474121094 ), it will return inf. this function is used to fix this bug.
*/
double atof(const char* str);
}
NS_CC_END

View File

@ -29,6 +29,7 @@ Copyright (c) 2013-2014 Chukong Technologies
#include <stdlib.h>
#include <stdio.h>
#include "CCArray.h"
#include "base/ccUtils.h"
NS_CC_BEGIN
@ -118,7 +119,7 @@ float __String::floatValue() const
{
return 0.0f;
}
return (float)atof(_string.c_str());
return (float)utils::atof(_string.c_str());
}
double __String::doubleValue() const
@ -127,7 +128,7 @@ double __String::doubleValue() const
{
return 0.0;
}
return atof(_string.c_str());
return utils::atof(_string.c_str());
}
bool __String::boolValue() const

View File

@ -28,6 +28,7 @@ THE SOFTWARE.
#include "ui/UIWidget.h"
#include "ui/UIHelper.h"
#include "cocostudio/CocoLoader.h"
#include "base/ccUtils.h"
using namespace cocos2d;
using namespace ui;
@ -184,7 +185,7 @@ void ActionNode::initWithDictionary(const rapidjson::Value& dic, Ref* root)
}
float ActionNode::valueToFloat(const std::string& value)
{
return atof(value.c_str());
return utils::atof(value.c_str());
}
void ActionNode::initWithBinary(CocoLoader *cocoLoader,

View File

@ -29,6 +29,7 @@ THE SOFTWARE.
#include "base/CCDirector.h"
#include "base/CCScheduler.h"
#include "2d/CCActionInstant.h"
#include "base/ccUtils.h"
using namespace cocos2d;
@ -189,7 +190,7 @@ bool ActionObject::valueToBool(const std::string& value)
}
float ActionObject::valueToFloat(const std::string& value)
{
return atof(value.c_str());
return utils::atof(value.c_str());
}
void ActionObject::addActionNode(ActionNode* node)

View File

@ -25,6 +25,7 @@ THE SOFTWARE.
#include "platform/CCFileUtils.h"
#include "base/CCDirector.h"
#include "base/CCScheduler.h"
#include "base/ccUtils.h"
#include "tinyxml2.h"
@ -1763,7 +1764,7 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
if (key.compare(CONTENT_SCALE) == 0)
{
std::string value = tpChildArray[i].GetValue(&tCocoLoader);
dataInfo->contentScale = atof(value.c_str());
dataInfo->contentScale = utils::atof(value.c_str());
}
else if ( 0 == key.compare(ARMATURE_DATA))
{
@ -1879,7 +1880,7 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
armatureData->name = name;
}
float version = atof(pAramtureDataArray[1].GetValue(cocoLoader));
float version = utils::atof(pAramtureDataArray[1].GetValue(cocoLoader));
dataInfo->cocoStudioVersion = armatureData->dataVersion = version; //DICTOOL->getFloatValue_json(json, VERSION, 0.1f);
int length = pAramtureDataArray[3].GetChildNum(); //DICTOOL->getArrayCount_json(json, BONE_DATA, 0);
@ -1989,27 +1990,27 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
str = SkinDataValue[i].GetValue(cocoLoader);
if (key.compare(A_X) == 0)
{
sdd->skinData.x = atof(str) * s_PositionReadScale;
sdd->skinData.x = utils::atof(str) * s_PositionReadScale;
}
else if (key.compare(A_Y) == 0)
{
sdd->skinData.y = atof(str) * s_PositionReadScale;
sdd->skinData.y = utils::atof(str) * s_PositionReadScale;
}
else if (key.compare(A_SCALE_X) == 0)
{
sdd->skinData.scaleX = atof(str);
sdd->skinData.scaleX = utils::atof(str);
}
else if (key.compare(A_SCALE_Y) == 0)
{
sdd->skinData.scaleY = atof(str);
sdd->skinData.scaleY = utils::atof(str);
}
else if (key.compare(A_SKEW_X) == 0)
{
sdd->skinData.skewX = atof(str);
sdd->skinData.skewX = utils::atof(str);
}
else if (key.compare(A_SKEW_Y) == 0)
{
sdd->skinData.skewY = atof(str);
sdd->skinData.skewY = utils::atof(str);
}
}
@ -2168,7 +2169,7 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
movementData->scale = 1.0;
if(str != nullptr)
{
movementData->scale = atof(str);
movementData->scale = utils::atof(str);
}
}
else if (key.compare(A_TWEEN_EASING) == 0)
@ -2220,7 +2221,7 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
{
if(str != nullptr)
{
movementBoneData->delay = atof(str);
movementBoneData->delay = utils::atof(str);
}
}
else if (key.compare(FRAME_DATA) == 0)
@ -2382,7 +2383,7 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
str = pFrameData[ii].GetValue(cocoLoader);
if (str != nullptr)
{
frameData->easingParams[ii] = atof(str);
frameData->easingParams[ii] = utils::atof(str);
}
}
}
@ -2421,28 +2422,28 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
{
if(str != nullptr)
{
textureData->width = atof(str);
textureData->width = utils::atof(str);
}
}
else if (key.compare(A_HEIGHT) == 0)
{
if(str != nullptr)
{
textureData->height = atof(str);
textureData->height = utils::atof(str);
}
}
else if (key.compare(A_PIVOT_X) == 0)
{
if(str != nullptr)
{
textureData->pivotX = atof(str);
textureData->pivotX = utils::atof(str);
}
}
else if (key.compare(A_PIVOT_Y) == 0)
{
if(str != nullptr)
{
textureData->pivotY = atof(str);
textureData->pivotY = utils::atof(str);
}
}
else if (key.compare(CONTOUR_DATA) == 0)
@ -2481,8 +2482,8 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
{
pVerTexPoint = pVerTexPointArray[ii].GetChildArray(cocoLoader);
Vec2 vertex;
vertex.x = atof(pVerTexPoint[0].GetValue(cocoLoader));
vertex.y = atof(pVerTexPoint[1].GetValue(cocoLoader));
vertex.x = utils::atof(pVerTexPoint[0].GetValue(cocoLoader));
vertex.y = utils::atof(pVerTexPoint[1].GetValue(cocoLoader));
contourData->vertexList.push_back(vertex); }
break;
}
@ -2505,11 +2506,11 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
str = child->GetValue(cocoLoader);
if (key.compare(A_X) == 0)
{
node->x = atof(str) * dataInfo->contentScale;
node->x = utils::atof(str) * dataInfo->contentScale;
}
else if (key.compare(A_Y) == 0)
{
node->y = atof(str) * dataInfo->contentScale;
node->y = utils::atof(str) * dataInfo->contentScale;
}
else if (key.compare(A_Z) == 0)
{
@ -2517,19 +2518,19 @@ void DataReaderHelper::decodeNode(BaseData *node, const rapidjson::Value& json,
}
else if (key.compare(A_SKEW_X) == 0)
{
node->skewX = atof(str);
node->skewX = utils::atof(str);
}
else if (key.compare(A_SKEW_Y) == 0)
{
node->skewY = atof(str);
node->skewY = utils::atof(str);
}
else if (key.compare(A_SCALE_X) == 0)
{
node->scaleX = atof(str);
node->scaleX = utils::atof(str);
}
else if (key.compare(A_SCALE_Y) == 0)
{
node->scaleY = atof(str);
node->scaleY = utils::atof(str);
}
else if (key.compare(COLOR_INFO) == 0)
{

View File

@ -1241,9 +1241,9 @@ Widget* WidgetPropertiesReader0300::createWidget(const rapidjson::Value& data, c
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(file);
}
}else if (key == "designWidth"){
fileDesignWidth = atof(tpChildArray[i].GetValue(cocoLoader));
fileDesignWidth = utils::atof(tpChildArray[i].GetValue(cocoLoader));
}else if (key == "designHeight"){
fileDesignHeight = atof(tpChildArray[i].GetValue(cocoLoader));
fileDesignHeight = utils::atof(tpChildArray[i].GetValue(cocoLoader));
}else if (key == "widgetTree"){
if (fileDesignWidth <= 0 || fileDesignHeight <= 0) {

View File

@ -510,12 +510,12 @@ void SceneReader::setPropertyFromJsonDict(CocoLoader *cocoLoader, stExpCocoNode
if (key == "x")
{
x = atof(value.c_str());
x = utils::atof(value.c_str());
node->setPositionX(x);
}
else if (key == "y")
{
y = atof(value.c_str());
y = utils::atof(value.c_str());
node->setPositionY(y);
}
else if (key == "visible")
@ -535,7 +535,7 @@ void SceneReader::setPropertyFromJsonDict(CocoLoader *cocoLoader, stExpCocoNode
}
else if(key == "scalex")
{
fScaleX = atof(value.c_str());
fScaleX = utils::atof(value.c_str());
node->setScaleX(fScaleX);
}
else if(key == "scaley")
@ -545,7 +545,7 @@ void SceneReader::setPropertyFromJsonDict(CocoLoader *cocoLoader, stExpCocoNode
}
else if(key == "rotation")
{
fRotationZ = atof(value.c_str());
fRotationZ = utils::atof(value.c_str());
node->setRotation(fRotationZ);
}
}

View File

@ -270,7 +270,7 @@ void TriggerMng::buildJson(rapidjson::Document &document, cocostudio::CocoLoader
else
{
int nV = atoi(str3);
float fV = atof(str3);
float fV = utils::atof(str3);
if (fabs(nV - fV) < 0.0000001)
{
dataitem.AddMember("value", nV, allocator);
@ -346,7 +346,7 @@ void TriggerMng::buildJson(rapidjson::Document &document, cocostudio::CocoLoader
else
{
int nV = atoi(str5);
float fV = atof(str5);
float fV = utils::atof(str5);
if (fabs(nV - fV) < 0.0000001)
{
dataitem.AddMember("value", nV, allocator);

View File

@ -87,7 +87,7 @@ namespace cocostudio
};
valueToFloat = [=](const std::string& str) -> float{
return atof(str.c_str());
return utils::atof(str.c_str());
};
}

View File

@ -31,6 +31,7 @@ THE SOFTWARE.
#include "base/ccMacros.h"
#include "base/CCDirector.h"
#include "platform/CCSAXParser.h"
#include "base/ccUtils.h"
#include "tinyxml2.h"
#include "unzip.h"
@ -257,7 +258,7 @@ public:
else if (sName == "integer")
_curArray->push_back(Value(atoi(_curValue.c_str())));
else
_curArray->push_back(Value(atof(_curValue.c_str())));
_curArray->push_back(Value(utils::atof(_curValue.c_str())));
}
else if (SAX_DICT == curState)
{
@ -266,7 +267,7 @@ public:
else if (sName == "integer")
(*_curDict)[_curKey] = Value(atoi(_curValue.c_str()));
else
(*_curDict)[_curKey] = Value(atof(_curValue.c_str()));
(*_curDict)[_curKey] = Value(utils::atof(_curValue.c_str()));
}
_curValue.clear();

View File

@ -30,6 +30,7 @@ THE SOFTWARE.
#include "base/CCEventKeyboard.h"
#include "base/CCEventMouse.h"
#include "base/CCIMEDispatcher.h"
#include "base/ccUtils.h"
#include <unordered_map>
@ -356,7 +357,7 @@ bool GLView::initWithRect(const std::string& viewName, Rect rect, float frameZoo
// check OpenGL version at first
const GLubyte* glVersion = glGetString(GL_VERSION);
if ( atof((const char*)glVersion) < 1.5 )
if ( utils::atof((const char*)glVersion) < 1.5 )
{
char strComplain[256] = {0};
sprintf(strComplain,

View File

@ -189,7 +189,7 @@ void TMoveTo::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpCo
{
if (str != nullptr)
{
_duration = atof(str);
_duration = utils::atof(str);
}
}
@ -197,7 +197,7 @@ void TMoveTo::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpCo
{
if (str != nullptr)
{
_pos.x = atof(str);
_pos.x = utils::atof(str);
}
}
else if (key == "y")
@ -311,7 +311,7 @@ void TMoveBy::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpCo
{
if (str != nullptr)
{
_duration = atof(str);
_duration = utils::atof(str);
}
}
@ -319,14 +319,14 @@ void TMoveBy::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpCo
{
if (str != nullptr)
{
_pos.x = atof(str);
_pos.x = utils::atof(str);
}
}
else if (key == "y")
{
if (str != nullptr)
{
_pos.y = atof(str);
_pos.y = utils::atof(str);
}
}
else if (key == "IsReverse")
@ -424,7 +424,7 @@ void TRotateTo::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExp
{
if (str != nullptr)
{
_duration = atof(str);
_duration = utils::atof(str);
}
}
@ -432,7 +432,7 @@ void TRotateTo::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExp
{
if (str != nullptr)
{
_deltaAngle = atof(str);
_deltaAngle = utils::atof(str);
}
}
}
@ -537,7 +537,7 @@ void TRotateBy::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExp
{
if (str != nullptr)
{
_duration = atof(str);
_duration = utils::atof(str);
}
}
@ -545,7 +545,7 @@ void TRotateBy::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExp
{
if (str != nullptr)
{
_deltaAngle = atof(str);
_deltaAngle = utils::atof(str);
}
}
else if (key == "IsReverse")
@ -647,7 +647,7 @@ void TScaleTo::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpC
{
if (str != nullptr)
{
_duration = atof(str);
_duration = utils::atof(str);
}
}
@ -655,14 +655,14 @@ void TScaleTo::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpC
{
if (str != nullptr)
{
_scale.x = atof(str);
_scale.x = utils::atof(str);
}
}
else if (key == "ScaleY")
{
if (str != nullptr)
{
_scale.y = atof(str);
_scale.y = utils::atof(str);
}
}
}
@ -771,7 +771,7 @@ void TScaleBy::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpC
{
if (str != nullptr)
{
_duration = atof(str);
_duration = utils::atof(str);
}
}
@ -779,14 +779,14 @@ void TScaleBy::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpC
{
if (str != nullptr)
{
_scale.x = atof(str);
_scale.x = utils::atof(str);
}
}
else if (key == "ScaleY")
{
if (str != nullptr)
{
_scale.y = atof(str);
_scale.y = utils::atof(str);
}
}
else if (key == "IsReverse")
@ -889,7 +889,7 @@ void TSkewTo::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpCo
{
if (str != nullptr)
{
_duration = atof(str);
_duration = utils::atof(str);
}
}
@ -897,14 +897,14 @@ void TSkewTo::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpCo
{
if (str != nullptr)
{
_skew.x = atof(str);
_skew.x = utils::atof(str);
}
}
else if (key == "SkewY")
{
if (str != nullptr)
{
_skew.y = atof(str);
_skew.y = utils::atof(str);
}
}
}
@ -1012,7 +1012,7 @@ void TSkewBy::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpCo
{
if (str != nullptr)
{
_duration = atof(str);
_duration = utils::atof(str);
}
}
@ -1020,14 +1020,14 @@ void TSkewBy::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stExpCo
{
if (str != nullptr)
{
_skew.x = atof(str);
_skew.x = utils::atof(str);
}
}
else if (key == "SkewY")
{
if (str != nullptr)
{
_skew.y = atof(str);
_skew.y = utils::atof(str);
}
}
else if (key == "IsReverse")

View File

@ -64,7 +64,7 @@ void TimeElapsed::serialize(cocostudio::CocoLoader *pCocoLoader, cocostudio::stE
{
if (str != nullptr)
{
_totalTime = atof(str); //DICTOOL->getFloatValue_json(subDict, "value");
_totalTime = utils::atof(str); //DICTOOL->getFloatValue_json(subDict, "value");
}
}
}