SUMO - Simulation of Urban MObility
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
NLTriggerBuilder.cpp
Go to the documentation of this file.
1 /****************************************************************************/
12 // Builds trigger objects for microsim
13 /****************************************************************************/
14 // SUMO, Simulation of Urban MObility; see http://sumo.dlr.de/
15 // Copyright (C) 2001-2016 DLR (http://www.dlr.de/) and contributors
16 /****************************************************************************/
17 //
18 // This file is part of SUMO.
19 // SUMO is free software: you can redistribute it and/or modify
20 // it under the terms of the GNU General Public License as published by
21 // the Free Software Foundation, either version 3 of the License, or
22 // (at your option) any later version.
23 //
24 /****************************************************************************/
25 
26 
27 // ===========================================================================
28 // included modules
29 // ===========================================================================
30 #ifdef _MSC_VER
31 #include <windows_config.h>
32 #else
33 #include <config.h>
34 #endif
35 
36 #include <string>
38 #include <microsim/MSLane.h>
39 #include <microsim/MSEdge.h>
40 #include <microsim/MSGlobals.h>
53 #include "NLHandler.h"
54 #include "NLTriggerBuilder.h"
56 #include <utils/xml/XMLSubSys.h>
57 
58 
59 #include <mesosim/MELoop.h>
61 
62 #ifdef CHECK_MEMORY_LEAKS
63 #include <foreign/nvwa/debug_new.h>
64 #endif // CHECK_MEMORY_LEAKS
65 
66 
67 // ===========================================================================
68 // method definitions
69 // ===========================================================================
71  : myHandler(0) {}
72 
73 
75 
76 void
78  myHandler = handler;
79 }
80 
81 
82 void
84  bool ok = true;
85  // get the id, throw if not given or empty...
86  std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
87  if (!ok) {
88  return;
89  }
90  MSEdge* e = MSEdge::dictionary(id);
91  if (e == 0) {
92  WRITE_ERROR("Unknown edge ('" + id + "') referenced in a vaporizer.");
93  return;
94  }
95  SUMOTime begin = attrs.getSUMOTimeReporting(SUMO_ATTR_BEGIN, 0, ok);
96  SUMOTime end = attrs.getSUMOTimeReporting(SUMO_ATTR_END, 0, ok);
97  if (!ok) {
98  return;
99  }
100  if (begin < 0) {
101  WRITE_ERROR("A vaporization begin time is negative (edge id='" + id + "').");
102  return;
103  }
104  if (begin >= end) {
105  WRITE_ERROR("A vaporization ends before it starts (edge id='" + id + "').");
106  return;
107  }
108  if (end >= string2time(OptionsCont::getOptions().getString("begin"))) {
113  }
114 }
115 
116 
117 
118 void
120  const std::string& base) {
121  // get the id, throw if not given or empty...
122  bool ok = true;
123  // get the id, throw if not given or empty...
124  std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
125  if (!ok) {
126  return;
127  }
128  // get the file name to read further definitions from
129  std::string file = getFileName(attrs, base, true);
130  std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANES, id.c_str(), ok);
131  if (!ok) {
132  throw InvalidArgument("The lanes to use within MSLaneSpeedTrigger '" + id + "' are not known.");
133  }
134  std::vector<MSLane*> lanes;
135  std::vector<std::string> laneIDs;
136  SUMOSAXAttributes::parseStringVector(objectid, laneIDs);
137  for (std::vector<std::string>::iterator i = laneIDs.begin(); i != laneIDs.end(); ++i) {
138  MSLane* lane = MSLane::dictionary(*i);
139  if (lane == 0) {
140  throw InvalidArgument("The lane to use within MSLaneSpeedTrigger '" + id + "' is not known.");
141  }
142  lanes.push_back(lane);
143  }
144  if (lanes.size() == 0) {
145  throw InvalidArgument("No lane defined for MSLaneSpeedTrigger '" + id + "'.");
146  }
147  try {
148  MSLaneSpeedTrigger* trigger = buildLaneSpeedTrigger(net, id, lanes, file);
149  if (file == "") {
151  }
152  } catch (ProcessError& e) {
153  throw InvalidArgument(e.what());
154  }
155 }
156 
157 void
159  bool ok = true;
160 
161  // get the id, throw if not given or empty...
162  std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
163 
164  if (!ok) {
165  throw ProcessError();
166  }
167 
168  // get the lane
169  MSLane* lane = getLane(attrs, "chargingStation", id);
170 
171  // get the positions
172  SUMOReal frompos = attrs.getOpt<SUMOReal>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
173  SUMOReal topos = attrs.getOpt<SUMOReal>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
174  SUMOReal chargingPower = attrs.getOpt<SUMOReal>(SUMO_ATTR_CHARGINGPOWER, id.c_str(), ok, 0);
175  SUMOReal efficiency = attrs.getOpt<SUMOReal>(SUMO_ATTR_EFFICIENCY, id.c_str(), ok, 0);
176  bool chargeInTransit = attrs.getOpt<bool>(SUMO_ATTR_CHARGEINTRANSIT, id.c_str(), ok, 0);
177  int ChargeDelay = attrs.getOpt<int>(SUMO_ATTR_CHARGEDELAY, id.c_str(), ok, 0);
178 
179  const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
180 
181  if (!ok || !myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos)) {
182  throw InvalidArgument("Invalid position for Charging Station '" + id + "'.");
183  }
184 
185  // build the Charging Station
186  buildChargingStation(net, id, lane, frompos, topos, chargingPower, efficiency, chargeInTransit, ChargeDelay);
187 }
188 
189 
190 void
192  bool ok = true;
193  // get the id, throw if not given or empty...
194  std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
195  if (!ok) {
196  throw ProcessError();
197  }
198  // get the lane
199  MSLane* lane = getLane(attrs, toString(element), id);
200  // get the positions
201  SUMOReal frompos = attrs.getOpt<SUMOReal>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
202  SUMOReal topos = attrs.getOpt<SUMOReal>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
203  const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
204  if (!ok || !myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos)) {
205  throw InvalidArgument("Invalid position for " + toString(element) + " '" + id + "'.");
206  }
207  // get the lines
208  std::vector<std::string> lines;
209  SUMOSAXAttributes::parseStringVector(attrs.getOpt<std::string>(SUMO_ATTR_LINES, id.c_str(), ok, "", false), lines);
210  // build the bus stop
211  buildStoppingPlace(net, id, lines, lane, frompos, topos, element);
212 }
213 
214 
215 void
217  // get the lane
218  MSLane* lane = getLane(attrs, "access" , "");
219  // get the positions
220  bool ok = true;
221  SUMOReal pos = attrs.getOpt<SUMOReal>(SUMO_ATTR_POSITION, "access", ok, 0);
222  const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, "access", ok, false);
223  if (!ok || !myHandler->checkStopPos(pos, pos, lane->getLength(), 0, friendlyPos)) {
224  throw InvalidArgument("Invalid position for access in stop '" + myCurrentStop->getID() + "'.");
225  }
226  // build the bus stop
227  myCurrentStop->addAccess(lane, pos);
228 }
229 
230 
231 void
233  const std::string& base) {
234  bool ok = true;
235  // get the id, throw if not given or empty...
236  std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
237  if (!ok) {
238  throw ProcessError();
239  }
240  // get the file name to read further definitions from
241  MSLane* lane = getLane(attrs, "calibrator", id);
242  const SUMOReal pos = getPosition(attrs, lane, "calibrator", id);
243  const SUMOTime freq = attrs.getOptSUMOTimeReporting(SUMO_ATTR_FREQUENCY, id.c_str(), ok, DELTA_T); // !!! no error handling
244  std::string file = getFileName(attrs, base, true);
245  std::string outfile = attrs.getOpt<std::string>(SUMO_ATTR_OUTPUT, id.c_str(), ok, "");
246  std::string routeProbe = attrs.getOpt<std::string>(SUMO_ATTR_ROUTEPROBE, id.c_str(), ok, "");
247  MSRouteProbe* probe = 0;
248  if (routeProbe != "") {
249  probe = dynamic_cast<MSRouteProbe*>(net.getDetectorControl().getTypedDetectors(SUMO_TAG_ROUTEPROBE).get(routeProbe));
250  }
252  METriggeredCalibrator* trigger = buildMECalibrator(net, id, &lane->getEdge(), pos, file, outfile, freq, probe);
253  if (file == "") {
255  }
256  } else {
257  MSCalibrator* trigger = buildCalibrator(net, id, &lane->getEdge(), pos, file, outfile, freq, probe);
258  if (file == "") {
260  }
261  }
262 }
263 
264 
265 void
267  const std::string& base) {
268  bool ok = true;
269  // get the id, throw if not given or empty...
270  std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
271  if (!ok) {
272  throw ProcessError();
273  }
274  // get the file name to read further definitions from
275  std::string file = getFileName(attrs, base, true);
276  std::string objectid = attrs.get<std::string>(SUMO_ATTR_EDGES, id.c_str(), ok);
277  if (!ok) {
278  throw InvalidArgument("The edge to use within MSTriggeredRerouter '" + id + "' is not known.");
279  }
280  MSEdgeVector edges;
281  std::vector<std::string> edgeIDs;
282  SUMOSAXAttributes::parseStringVector(objectid, edgeIDs);
283  for (std::vector<std::string>::iterator i = edgeIDs.begin(); i != edgeIDs.end(); ++i) {
284  MSEdge* edge = MSEdge::dictionary(*i);
285  if (edge == 0) {
286  throw InvalidArgument("The edge to use within MSTriggeredRerouter '" + id + "' is not known.");
287  }
288  edges.push_back(edge);
289  }
290  if (edges.size() == 0) {
291  throw InvalidArgument("No edges found for MSTriggeredRerouter '" + id + "'.");
292  }
293  SUMOReal prob = attrs.getOpt<SUMOReal>(SUMO_ATTR_PROB, id.c_str(), ok, 1);
294  bool off = attrs.getOpt<bool>(SUMO_ATTR_OFF, id.c_str(), ok, false);
295  if (!ok) {
296  throw InvalidArgument("Could not parse MSTriggeredRerouter '" + id + "'.");
297  }
298  MSTriggeredRerouter* trigger = buildRerouter(net, id, edges, prob, file, off);
299  // read in the trigger description
300  if (file == "") {
302  } else if (!XMLSubSys::runParser(*trigger, file)) {
303  throw ProcessError();
304  }
305 }
306 
307 
308 // -------------------------
309 
310 
312 NLTriggerBuilder::buildLaneSpeedTrigger(MSNet& /*net*/, const std::string& id,
313  const std::vector<MSLane*>& destLanes,
314  const std::string& file) {
315  return new MSLaneSpeedTrigger(id, destLanes, file);
316 }
317 
318 
320 NLTriggerBuilder::buildMECalibrator(MSNet& /*net*/, const std::string& id,
321  const MSEdge* edge, SUMOReal pos,
322  const std::string& file,
323  const std::string& outfile,
324  const SUMOTime freq, MSRouteProbe* probe) {
325  return new METriggeredCalibrator(id, edge, pos, file, outfile, freq, MSGlobals::gMesoNet->getSegmentForEdge(*edge, pos)->getLength(), probe);
326 }
327 
328 
330 NLTriggerBuilder::buildCalibrator(MSNet& /*net*/, const std::string& id,
331  MSEdge* edge, SUMOReal pos,
332  const std::string& file,
333  const std::string& outfile,
334  const SUMOTime freq, const MSRouteProbe* probe) {
335  return new MSCalibrator(id, edge, pos, file, outfile, freq, edge->getLength(), probe);
336 }
337 
338 
340 NLTriggerBuilder::buildRerouter(MSNet&, const std::string& id,
341  MSEdgeVector& edges,
342  SUMOReal prob, const std::string& file, bool off) {
343  return new MSTriggeredRerouter(id, edges, prob, file, off);
344 }
345 
346 
347 void
348 NLTriggerBuilder::buildStoppingPlace(MSNet& net, const std::string& id,
349  const std::vector<std::string>& lines,
350  MSLane* lane, SUMOReal frompos, SUMOReal topos, const SumoXMLTag element) {
351  myCurrentStop = new MSStoppingPlace(id, lines, *lane, frompos, topos);
352  const bool success = element == SUMO_TAG_CONTAINER_STOP ? net.addContainerStop(myCurrentStop) : net.addBusStop(myCurrentStop);
353  if (!success) {
354  delete myCurrentStop;
355  throw InvalidArgument("Could not build " + toString(element) + " '" + id + "'; probably declared twice.");
356  }
357 }
358 
359 
360 void
361 NLTriggerBuilder::buildChargingStation(MSNet& net, const std::string& id, MSLane* lane, SUMOReal frompos, SUMOReal topos,
362  SUMOReal chargingPower, SUMOReal efficiency, bool chargeInTransit, int ChargeDelay) {
363  MSChargingStation* chargingStation = new MSChargingStation(id, *lane, frompos, topos, chargingPower, efficiency, chargeInTransit, ChargeDelay);
364 
365  if (!net.addChargingStation(chargingStation)) {
366  delete chargingStation;
367  throw InvalidArgument("Could not build Charging Station '" + id + "'; probably declared twice.");
368  }
369 }
370 
371 std::string
373  const std::string& base,
374  const bool allowEmpty) {
375  // get the file name to read further definitions from
376  bool ok = true;
377  std::string file = attrs.getOpt<std::string>(SUMO_ATTR_FILE, 0, ok, "");
378  if (file == "") {
379  if (allowEmpty) {
380  return file;
381  }
382  throw InvalidArgument("No filename given.");
383  }
384  // check whether absolute or relative filenames are given
385  if (!FileHelpers::isAbsolute(file)) {
386  return FileHelpers::getConfigurationRelative(base, file);
387  }
388  return file;
389 }
390 
391 
392 MSLane*
394  const std::string& tt,
395  const std::string& tid) {
396  bool ok = true;
397  std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANE, tid.c_str(), ok);
398  MSLane* lane = MSLane::dictionary(objectid);
399  if (lane == 0) {
400  throw InvalidArgument("The lane " + objectid + " to use within the " + tt + " '" + tid + "' is not known.");
401  }
402  return lane;
403 }
404 
405 
406 SUMOReal
408  MSLane* lane,
409  const std::string& tt, const std::string& tid) {
410  bool ok = true;
411  SUMOReal pos = attrs.get<SUMOReal>(SUMO_ATTR_POSITION, 0, ok);
412  const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, 0, ok, false);
413  if (!ok) {
414  throw InvalidArgument("Error on parsing a position information.");
415  }
416  if (pos < 0) {
417  pos = lane->getLength() + pos;
418  }
419  if (pos > lane->getLength()) {
420  if (friendlyPos) {
421  pos = lane->getLength() - (SUMOReal) 0.1;
422  } else {
423  throw InvalidArgument("The position of " + tt + " '" + tid + "' lies beyond the lane's '" + lane->getID() + "' length.");
424  }
425  }
426  return pos;
427 }
428 
429 
430 
431 /****************************************************************************/
void setHandler(NLHandler *handler)
Sets the parent handler to use for nested parsing.
void parseAndBuildCalibrator(MSNet &net, const SUMOSAXAttributes &attrs, const std::string &base)
Parses his values and builds a mesoscopic or microscopic calibrator.
SumoXMLTag
Numbers representing SUMO-XML - element names.
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:571
virtual MSCalibrator * buildCalibrator(MSNet &net, const std::string &id, MSEdge *edge, SUMOReal pos, const std::string &file, const std::string &outfile, const SUMOTime freq, const MSRouteProbe *probe)
builds a microscopic calibrator
virtual void buildChargingStation(MSNet &net, const std::string &id, MSLane *lane, SUMOReal frompos, SUMOReal topos, SUMOReal chargingPower, SUMOReal efficiency, bool chargeInTransit, int ChargeDelay)
Builds a charging Station.
long long int SUMOTime
Definition: SUMOTime.h:43
static std::string getConfigurationRelative(const std::string &configPath, const std::string &path)
Returns the second path as a relative path to the first file.
Definition: FileHelpers.cpp:86
void addAccess(MSNet &net, const SUMOSAXAttributes &attrs)
Parses the values and adds an access point to the currently parsed stopping place.
A lane area vehicles can halt at.
Writes routes of vehicles passing a certain edge.
Definition: MSRouteProbe.h:68
SUMOReal getLength() const
Returns the lane's length.
Definition: MSLane.h:480
void parseAndBuildChargingStation(MSNet &net, const SUMOSAXAttributes &attrs)
Parses his values and builds a charging station.
void parseAndBuildRerouter(MSNet &net, const SUMOSAXAttributes &attrs, const std::string &base)
Parses his values and builds a rerouter.
static bool checkStopPos(SUMOReal &startPos, SUMOReal &endPos, const SUMOReal laneLength, const SUMOReal minLength, const bool friendlyPos)
check start and end position of a stop
bool addBusStop(MSStoppingPlace *busStop)
Adds a bus stop.
Definition: MSNet.cpp:783
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:159
SUMOTime DELTA_T
Definition: SUMOTime.cpp:39
static bool dictionary(const std::string &id, MSEdge *edge)
Inserts edge into the static dictionary Returns true if the key id isn't already in the dictionary...
Definition: MSEdge.cpp:673
SUMOTime incVaporization(SUMOTime t)
Enables vaporization.
Definition: MSEdge.cpp:383
NLTriggerBuilder()
Constructor.
Base (microsim) event class.
Definition: Command.h:61
static bool runParser(GenericSAXHandler &handler, const std::string &file, const bool isNet=false)
Runs the given handler on the given file; returns if everything's ok.
Definition: XMLSubSys.cpp:114
virtual ~NLTriggerBuilder()
Destructor.
SUMOTime decVaporization(SUMOTime t)
Disables vaporization.
Definition: MSEdge.cpp:390
The simulated network and simulation perfomer.
Definition: MSNet.h:93
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:69
SUMOTime getOptSUMOTimeReporting(int attr, const char *objectid, bool &ok, SUMOTime defaultValue, bool report=true) const
Tries to read given attribute assuming it is a SUMOTime.
Changes the speed allowed on a set of lanes.
T get(const std::string &id) const
Retrieves an item.
virtual void buildStoppingPlace(MSNet &net, const std::string &id, const std::vector< std::string > &lines, MSLane *lane, SUMOReal frompos, SUMOReal topos, const SumoXMLTag element)
Builds a stopping place.
const std::string & getID() const
Returns the id.
Definition: Named.h:66
A road/street connecting two junctions.
Definition: MSEdge.h:80
const NamedObjectCont< MSDetectorFileOutput * > & getTypedDetectors(SumoXMLTag type) const
Returns the list of detectors of the given type.
static void parseStringVector(const std::string &def, std::vector< std::string > &into)
Splits the given string.
SUMOReal getLength() const
return the length of the edge
Definition: MSEdge.h:591
SUMOReal getPosition(const SUMOSAXAttributes &attrs, MSLane *lane, const std::string &tt, const std::string &tid)
returns the position on the lane checking it
the edges of a route
Encapsulated SAX-Attributes.
Calibrates the flow on a segment to a specified one.
NLHandler * myHandler
The parent handler to set for subhandlers.
static bool isAbsolute(const std::string &path)
Returns the information whether the given path is absolute.
MSEventControl * getBeginOfTimestepEvents()
Returns the event control for events executed at the begin of a time step.
Definition: MSNet.h:400
A wrapper for a Command function.
void parseAndBuildLaneSpeedTrigger(MSNet &net, const SUMOSAXAttributes &attrs, const std::string &base)
Parses his values and builds a lane speed trigger.
void registerParent(const int tag, GenericSAXHandler *handler)
Assigning a parent handler which is enabled when the specified tag is closed.
MSStoppingPlace * myCurrentStop
The currently parsed stop to add access points to.
SUMOTime string2time(const std::string &r)
Definition: SUMOTime.cpp:46
void parseAndBuildStoppingPlace(MSNet &net, const SUMOSAXAttributes &attrs, const SumoXMLTag element)
Parses the values and builds a stopping places for busses, trains or container vehicles.
#define POSITION_EPS
Definition: config.h:188
virtual void addAccess(MSLane *lane, const SUMOReal pos)
adds an access point to this stop
std::string toString(const T &t, std::streamsize accuracy=OUTPUT_ACCURACY)
Definition: ToString.h:55
SUMOTime getSUMOTimeReporting(int attr, const char *objectid, bool &ok, bool report=true) const
Tries to read given attribute assuming it is a SUMOTime.
MSDetectorControl & getDetectorControl()
Returns the detector control.
Definition: MSNet.h:370
std::string getFileName(const SUMOSAXAttributes &attrs, const std::string &base, const bool allowEmpty=false)
Helper method to obtain the filename.
virtual SUMOTime addEvent(Command *operation, SUMOTime execTimeStep, AdaptType type)
Adds an Event.
The XML-Handler for network loading.
Definition: NLHandler.h:84
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:206
Reroutes vehicles passing an edge.
static bool dictionary(const std::string &id, MSLane *lane)
Static (sic!) container methods {.
Definition: MSLane.cpp:1286
bool addContainerStop(MSStoppingPlace *containerStop)
Adds a container stop.
Definition: MSNet.cpp:808
bool addChargingStation(MSChargingStation *chargingStation)
Adds a chargingg station.
Definition: MSNet.cpp:831
virtual METriggeredCalibrator * buildMECalibrator(MSNet &net, const std::string &id, const MSEdge *edge, SUMOReal pos, const std::string &file, const std::string &outfile, const SUMOTime freq, MSRouteProbe *probe)
builds a mesoscopic calibrator
static MELoop * gMesoNet
mesoscopic simulation infrastructure
Definition: MSGlobals.h:107
Calibrates the flow on a segment to a specified one.
Definition: MSCalibrator.h:57
A variable speed sign.
Patch the time in a way that it is at least as high as the simulation begin time. ...
#define SUMOReal
Definition: config.h:214
virtual MSTriggeredRerouter * buildRerouter(MSNet &net, const std::string &id, MSEdgeVector &edges, SUMOReal prob, const std::string &file, bool off)
builds an rerouter
T getOpt(int attr, const char *objectid, bool &ok, T defaultValue, bool report=true) const
Tries to read given attribute assuming it is an int.
virtual MSLaneSpeedTrigger * buildLaneSpeedTrigger(MSNet &net, const std::string &id, const std::vector< MSLane * > &destLanes, const std::string &file)
Builds a lane speed trigger.
std::vector< MSEdge * > MSEdgeVector
Definition: MSEdge.h:77
T get(int attr, const char *objectid, bool &ok, bool report=true) const
Tries to read given attribute assuming it is an int.
static bool gUseMesoSim
Definition: MSGlobals.h:95
Representation of a lane in the micro simulation.
Definition: MSLane.h:79
void buildVaporizer(const SUMOSAXAttributes &attrs)
Builds a vaporization.
MSLane * getLane(const SUMOSAXAttributes &attrs, const std::string &tt, const std::string &tid)
Returns the lane defined by attribute "lane".