vector<SurfaceSample> SurfaceSample::loadFromXML(const std::string& filename) { vector<SurfaceSample> samples; try { PTree tree; read_xml(filename, tree); PTree root = tree.get_child("SurfaceSamples"); for (CI p = root.begin(); p != root.end(); ++p) { if (p->first == "Sample") { Q posq = XMLHelpers::readQ(p->second.get_child("Pos")); Q rpyq = XMLHelpers::readQ(p->second.get_child("RPY")); double graspW = XMLHelpers::readDouble(p->second.get_child("GraspW")); //cout << "pos=" << posq << " rot=" << rpyq << " graspW=" << graspW << endl; Vector3D<> pos(posq[0], posq[1], posq[2]); RPY<> rpy(rpyq[0], rpyq[1], rpyq[2]); samples.push_back(SurfaceSample(Transform3D<>(pos, rpy.toRotation3D()), graspW)); } } } catch (const ptree_error& e) { RW_THROW(e.what()); } return samples; }
static void write_ini(const PTree & p, std::ostream & out, std::string key_name) { for (PTree::const_iterator i = p.begin(), e = p.end(); i != e; ++i) { if (i->second.size() == 0) { out << i->first << " = " << i->second.value() << "\n"; } } out << "\n"; for (PTree::const_iterator i = p.begin(), e = p.end(); i != e; ++i) { if (i->second.size() > 0) { out << "[" << key_name + i->first << "]\n"; write_ini(i->second, out, key_name + i->first + "."); } } }
static void write_xml(const PTree & p, std::ostream & out, std::string indent) { for (PTree::const_iterator i = p.begin(), e = p.end(); i != e; ++i) { if (i->second.size() == 0) { out << indent << "<" << i->first << ">" << i->second.value() << "</" << i->first << ">\n"; write_xml(i->second, out, indent+"\t"); } else { out << indent << "<" << i->first << ">\n"; write_xml(i->second, out, indent+"\t"); out << indent << "</" << i->first << ">\n"; } } }
bool CAR::LoadSounds( const std::string & carpath, const std::string & carname, SOUND & sound, ContentManager & content, std::ostream & error_output) { psound = &sound; // check for sound specification file std::string path_aud = carpath + "/" + carname + ".aud"; std::ifstream file_aud(path_aud.c_str()); if (file_aud.good()) { PTree aud; read_ini(file_aud, aud); enginesounds.reserve(aud.size()); for (PTree::const_iterator i = aud.begin(); i != aud.end(); ++i) { const PTree & audi = i->second; std::string filename; std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!audi.get("filename", filename, error_output)) return false; enginesounds.push_back(ENGINESOUNDINFO()); ENGINESOUNDINFO & info = enginesounds.back(); if (!audi.get("MinimumRPM", info.minrpm, error_output)) return false; if (!audi.get("MaximumRPM", info.maxrpm, error_output)) return false; if (!audi.get("NaturalRPM", info.naturalrpm, error_output)) return false; bool powersetting; if (!audi.get("power", powersetting, error_output)) return false; if (powersetting) info.power = ENGINESOUNDINFO::POWERON; else if (!powersetting) info.power = ENGINESOUNDINFO::POWEROFF; else info.power = ENGINESOUNDINFO::BOTH; info.sound_source = sound.AddSource(soundptr, 0, true, true); sound.SetSourceGain(info.sound_source, 0); } // set blend start and end locations -- requires multiple passes std::map <ENGINESOUNDINFO *, ENGINESOUNDINFO *> temporary_to_actual_map; std::list <ENGINESOUNDINFO> poweron_sounds, poweroff_sounds; for (std::vector <ENGINESOUNDINFO>::iterator i = enginesounds.begin(); i != enginesounds.end(); ++i) { if (i->power == ENGINESOUNDINFO::POWERON) { poweron_sounds.push_back(*i); temporary_to_actual_map[&poweron_sounds.back()] = &*i; } else if (i->power == ENGINESOUNDINFO::POWEROFF) { poweroff_sounds.push_back(*i); temporary_to_actual_map[&poweroff_sounds.back()] = &*i; } } poweron_sounds.sort(); poweroff_sounds.sort(); // we only support 2 overlapping sounds at once each for poweron and poweroff; this // algorithm fails for other cases (undefined behavior) std::list <ENGINESOUNDINFO> * cursounds = &poweron_sounds; for (int n = 0; n < 2; n++) { if (n == 1) cursounds = &poweroff_sounds; for (std::list <ENGINESOUNDINFO>::iterator i = (*cursounds).begin(); i != (*cursounds).end(); ++i) { // set start blend if (i == (*cursounds).begin()) i->fullgainrpmstart = i->minrpm; // set end blend std::list <ENGINESOUNDINFO>::iterator inext = i; inext++; if (inext == (*cursounds).end()) i->fullgainrpmend = i->maxrpm; else { i->fullgainrpmend = inext->minrpm; inext->fullgainrpmstart = i->maxrpm; } } // now assign back to the actual infos for (std::list <ENGINESOUNDINFO>::iterator i = (*cursounds).begin(); i != (*cursounds).end(); ++i) { assert(temporary_to_actual_map.find(&(*i)) != temporary_to_actual_map.end()); *temporary_to_actual_map[&(*i)] = *i; } } } else { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!content.load(carpath, "engine", soundptr)) return false; enginesounds.push_back(ENGINESOUNDINFO()); enginesounds.back().sound_source = sound.AddSource(soundptr, 0, true, true); } //set up tire squeal sounds for (int i = 0; i < 4; ++i) { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!content.load(carpath, "tire_squeal", soundptr)) return false; tiresqueal[i] = sound.AddSource(soundptr, i * 0.25, true, true); } //set up tire gravel sounds for (int i = 0; i < 4; ++i) { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!content.load(carpath, "gravel", soundptr)) return false; gravelsound[i] = sound.AddSource(soundptr, i * 0.25, true, true); } //set up tire grass sounds for (int i = 0; i < 4; ++i) { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!content.load(carpath, "grass", soundptr)) return false; grasssound[i] = sound.AddSource(soundptr, i * 0.25, true, true); } //set up bump sounds for (int i = 0; i < 4; ++i) { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (i >= 2) { if (!content.load(carpath, "bump_rear", soundptr)) return false; } else { if (!content.load(carpath, "bump_front", soundptr)) return false; } tirebump[i] = sound.AddSource(soundptr, 0, true, false); } //set up crash sound { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!content.load(carpath, "crash", soundptr)) return false; crashsound = sound.AddSource(soundptr, 0, true, false); } //set up gear sound { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!content.load(carpath, "gear", soundptr)) return false; gearsound = sound.AddSource(soundptr, 0, true, false); } //set up brake sound { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!content.load(carpath, "brake", soundptr)) return false; brakesound = sound.AddSource(soundptr, 0, true, false); } //set up handbrake sound { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!content.load(carpath, "handbrake", soundptr)) return false; handbrakesound = sound.AddSource(soundptr, 0, true, false); } { std::tr1::shared_ptr<SOUNDBUFFER> soundptr; if (!content.load(carpath, "wind", soundptr)) return false; roadnoise = sound.AddSource(soundptr, 0, true, true); } return true; }
bool CAR::LoadGraphics( const PTree & cfg, const std::string & partspath, const std::string & carpath, const std::string & carname, const std::string & carpaint, const MATHVECTOR <float, 3> & carcolor, const int anisotropy, const float camerabounce, const bool damage, const bool debugmode, ContentManager & content, std::ostream & info_output, std::ostream & error_output) { //write_inf(cfg, std::cerr); cartype = carname; LoadDrawable loadDrawable(carpath, anisotropy, content, models, error_output); // load body first const PTree * cfg_body; std::string meshname; std::vector<std::string> texname; if (!cfg.get("body", cfg_body, error_output)) { error_output << "there is a problem with the .car file" << std::endl; return false; } if (!cfg_body->get("mesh", meshname, error_output)) return false; if (!cfg_body->get("texture", texname, error_output)) return false; if (carpaint != "default") texname[0] = carpaint; if (!loadDrawable(meshname, texname, *cfg_body, topnode, &bodynode)) return false; // load wheels const PTree * cfg_wheel; if (!cfg.get("wheel", cfg_wheel, error_output)) return false; for (PTree::const_iterator i = cfg_wheel->begin(); i != cfg_wheel->end(); ++i) { if (!LoadWheel(i->second, loadDrawable, topnode, error_output)) { error_output << "Failed to load wheels." << std::endl; return false; } } // load drawables LoadBody loadBody(topnode, bodynode, loadDrawable); for (PTree::const_iterator i = cfg.begin(); i != cfg.end(); ++i) { if (i->first != "body" && i->first != "steering" && i->first != "light-brake" && i->first != "light-reverse") { loadBody(i->second); } } // load steering wheel const PTree * cfg_steer; if (cfg.get("steering", cfg_steer)) { SCENENODE & bodynoderef = topnode.GetNode(bodynode); if (!loadDrawable(*cfg_steer, bodynoderef, &steernode, 0)) { error_output << "Failed to load steering wheel." << std::endl; return false; } cfg_steer->get("max-angle", steer_angle_max); steer_angle_max = steer_angle_max / 180.0 * M_PI; SCENENODE & steernoderef = bodynoderef.GetNode(steernode); steer_orientation = steernoderef.GetTransform().GetRotation(); } // load brake/reverse light point light sources (optional) int i = 0; std::string istr = "0"; const PTree * cfg_light; while (cfg.get("light-brake-"+istr, cfg_light)) { if (!LoadLight(*cfg_light, content, error_output)) { error_output << "Failed to load lights." << std::endl; return false; } std::stringstream sstr; sstr << ++i; istr = sstr.str(); } i = 0; istr = "0"; while (cfg.get("light-reverse-"+istr, cfg_light)) { if (!LoadLight(*cfg_light, content, error_output)) { error_output << "Failed to load lights." << std::endl; return false; } std::stringstream sstr; sstr << ++i; istr = sstr.str(); } // load car brake/reverse graphics (optional) if (cfg.get("light-brake", cfg_light)) { SCENENODE & bodynoderef = topnode.GetNode(bodynode); if (!loadDrawable(*cfg_light, bodynoderef, 0, &brakelights)) { error_output << "Failed to load lights." << std::endl; return false; } } if (cfg.get("light-reverse", cfg_light)) { SCENENODE & bodynoderef = topnode.GetNode(bodynode); if (!loadDrawable(*cfg_light, bodynoderef, 0, &reverselights)) { error_output << "Failed to load lights." << std::endl; return false; } } const PTree * cfg_cams; if (!cfg.get("camera", cfg_cams)) { return false; } if (!cfg_cams->size()) { error_output << "No cameras defined." << std::endl; return false; } cameras.reserve(cfg_cams->size()); for (PTree::const_iterator i = cfg_cams->begin(); i != cfg_cams->end(); ++i) { CAMERA * cam = LoadCamera(i->second, camerabounce, error_output); if (!cam) return false; cameras.push_back(cam); } SetColor(carcolor[0], carcolor[1], carcolor[2]); return true; }
int main(int argc, char* argv[]) { if (argc != 4) { std::cout << "Aufruf mit Parametern: <französiche Trainigsdaten> <englische Trainingsdaten> <Alignment der Trainingsdaten>\n" << "Folgende Ausgabe: relfreq_f relfreq_e # quellphrase # zielphrase # singlecf singlece # source_to_target target_to_source # unigram-sprachmodell\n"; return 0; } Lexicon flex(french); Lexicon elex(english); PTree<std::pair<int, PTree<int> > > pTree; PTree<unsigned int> eSinglecount; uint eCount = 0; //Gesamtzahl der englischen Wörter std::unordered_map<uint,Wordinfo> ef_pair, fe_pair; //Einzelwortbasierte Übersetzungshäufigkeit von e nach f (und umgekehrt) igzstream f_in(argv[1]), e_in(argv[2]), a_in(argv[3]); std::string f_line, e_line, a_line; while (getline(f_in, f_line) && getline(e_in, e_line)) { /*==========Lies Wörter beider Sätze, sowie zugehörige Alignments aus entsprechenden Dateien aus==========*/ std::string token; std:: istringstream f_ist(f_line), e_ist(e_line); std::vector<std::pair<uint, std::vector<unsigned int> > > f_vec, e_vec; //Speichern alle Wörter jeweiliger Sprache und ihre Alignments //Füge alle französichen Wörter in ein Lexicon ein und schreibe ihre IDs in einen Vektor while(f_ist >> token) { uint id = flex.getWord_or_add(token); std::pair<uint, std::vector<unsigned int> > pair_tmp; pair_tmp.first = id; f_vec.push_back(pair_tmp); } //Füge alle englischen Wörter in ein Lexicon ein und schreibe ihre IDs in einen Vektor while (e_ist >> token) { uint id = elex.getWord_or_add(token); std::pair<uint, std::vector<unsigned int> > pair_tmp; pair_tmp.first = id; e_vec.push_back(pair_tmp); eCount++; } getline(a_in, a_line); //"SEND:" abfangen do { getline(a_in, a_line); if(a_line == "") break; //Alignment eine Satzes zu Ende else { std::istringstream a_ist(a_line); int f_ind, e_ind; std::string s; a_ist >> s >> f_ind >> e_ind; f_vec[f_ind].second.push_back(e_ind); //Speichere einzelnes Alignment in f_vec e_vec[e_ind].second.push_back(f_ind); //Speichere einzelnes Alignment in e_vec uint& f_id = f_vec[f_ind].first, e_id = e_vec[e_ind].first; fe_pair[f_id].pairs[e_id]++; //Paircount für f nach e erhöhen fe_pair[f_id].singlecount++; //Singlecount für f nach e erhöhen ef_pair[e_id].pairs[f_id]++; //Paircount für e nach f erhöhen ef_pair[e_id].singlecount++; //Singlecount für e nach f erhöhen } } while(true); /*==========Beide Sätze liegen nun in Vektoren gespeichert vor, die Alignments jedes Wortes sind in einem Vektor gespeichert========== *==========Führe darauf nun den vorgegebenen Algorithmus aus, um alle Phrasen zu finden und im Präfixbaum zu speichern==========*/ for(unsigned int j1 = 0; j1 < f_vec.size(); j1++) for(unsigned int j2 = j1; j2 < std::min(j1+3,(unsigned int)f_vec.size()); j2++) { //Länge der Quellphrase maximal 3 unsigned int i1, i2; bool set_i = false; //hält mit, ob i1 und i2 gesetzt wurden, oder nicht. for(unsigned int k = j1; k <= j2; k++) if(f_vec[k].second.size() && set_i) { i1 = std::min(i1, f_vec[k].second.front()); //Minimales Alignment innerhalb der Phrase finden => i1 i2 = std::max(i2, f_vec[k].second.back()); //Maximales Alignment innerhalb der Phrase finden => i2 } else if(f_vec[k].second.size() && !(set_i)) { i1 = f_vec[k].second.front(); i2 = f_vec[k].second.back(); set_i = true; } if (set_i){ //leere Phrasen werden nicht geprüft sondern direkt verworfen if(j1 == j2) { //Einzelwortphrasen auf Quellseite werden IMMER extrahiert std::vector<uint> f_vec_tmp, e_vec_tmp; for (unsigned int k = j1; k <= j2; k++) f_vec_tmp.push_back(f_vec[k].first); //Quellphrase in Vektor zusammenstellen for (unsigned int k = i1; k <= i2; k++) e_vec_tmp.push_back(e_vec[k].first); //Zielphrase in Vektor zusammenstellen std::pair<int, PTree<int> > pair_tmp; pair_tmp.first = 0; pTree.traverse(f_vec_tmp,true,pair_tmp)->c.first++; //Quellphrase in Baum einfügen pTree.traverse(f_vec_tmp,false)->c.second.traverse(e_vec_tmp,true,0)->c++; //Zielphrase in "Unter-Baum" einfügen eSinglecount.traverse(e_vec_tmp,true,0)->c++; } else if (i2-i1 < 4) { //Länge der Zielphrase maximal 4 unsigned int j1_test, j2_test; bool set_j_test = false; //hält mit, ob j1_test und j2_test gesetzt wurden, oder nicht for (unsigned int k = i1; k <= i2; k++) if (e_vec[k].second.size() && set_j_test) { j1_test = std::min(j1_test, e_vec[k].second.front()); j2_test = std::max(j2_test, e_vec[k].second.back()); } else if (e_vec[k].second.size() && !(set_j_test)) { j1_test = e_vec[k].second.front(); j2_test = e_vec[k].second.back(); set_j_test = true; } if (set_j_test) //leere Phrasen werden nicht geprüft sondern sofort verworfen if ((j1_test >= j1) && (j2_test <= j2)) { //Phrasen, die den Test bestehen, werden extrahiert std::vector<uint> f_vec_tmp, e_vec_tmp; for (unsigned int k = j1; k <= j2; k++) f_vec_tmp.push_back(f_vec[k].first); for (unsigned int k = i1; k <= i2; k++) e_vec_tmp.push_back(e_vec[k].first); std::pair<int, PTree<int> > pair_tmp; pair_tmp.first = 0; pTree.traverse(f_vec_tmp,true,pair_tmp)->c.first++; //Quellphrase in Baum einfügen pTree.traverse(f_vec_tmp,false)->c.second.traverse(e_vec_tmp,true,0)->c++; //Zielphrase in "Unter-Baum" einfügen eSinglecount.traverse(e_vec_tmp,true,0)->c++; } } } } /*==========Jetzt sind alle erlaubten Phrasen aus diesem Satzpaar im Präfixbaum gespeichert==========*/ /*==========Damit ist die Bearbeitung dieses Satzpaares abgeschlossen und das nächste rückt nach==========*/ } /*==========Nun sind alle erlaubten Phrasen aller Satzpaare - also der gesamten Testdaten - im Präfixbaum gespeichert==========*/ /*==========Im Anschluss muss also der Präfixbaum in eine Phrasentabelle ausgegeben werden==========*/ for (PTree<std::pair<int, PTree<int> > >::iterator itor1 = pTree.begin(); itor1 != pTree.end(); itor1++) { //Durchlaufe den Baum int singlecount_f = (&*itor1) -> c.first; //Zähler für Quellphrase auslesen if (singlecount_f) { std::vector<uint> source_id = (&*itor1) -> phrase();//Quellphrase (in IDs) auslesen std::string source_phrase = ""; for (unsigned int k = 0; k < source_id.size(); k++) //ID-Phrase in Stringphrase umwandeln source_phrase += flex.getString(source_id[k]) + " "; for(PTree<int>::iterator itor2 = (&*itor1) -> c.second.begin(); itor2 != (&*itor1) -> c.second.end(); itor2++) { //Durchlaufe den "Unter-Baum" int paircount = (&*itor2) -> c; //Zähler für Zielphrase auslesen if(paircount != 0) { std::vector<uint> target_id = (&*itor2) -> phrase();//Zielphrase (in IDs) auslesen std::string target_phrase = ""; for (unsigned int k = 0; k < target_id.size(); k++) //ID-Phrase in Stringphrase umwandeln target_phrase += elex.getString(target_id[k]) + " "; double source_to_target = 1; for (unsigned int k = 0; k < target_id.size(); k++) { double sum_stt = 0; for (unsigned int l = 0; l < source_id.size(); l++) { sum_stt += (double) fe_pair[source_id[l]].pairs[target_id[k]] / (double) fe_pair[source_id[l]].singlecount; } source_to_target *= sum_stt / source_id.size(); } source_to_target = -log(source_to_target); double target_to_source = 1; for (unsigned int k = 0; k < source_id.size(); k++) { double sum_tts = 0; for (unsigned int l = 0; l < target_id.size(); l++) { sum_tts += (double) ef_pair[target_id[l]].pairs[source_id[k]] / (double) ef_pair[target_id[l]].singlecount; } target_to_source *= sum_tts / target_id.size(); } target_to_source = -log(target_to_source); uint singlecount_e = eSinglecount.traverse(target_id)->c; double relFreqF = log(singlecount_f) - log(paircount); //Bestimmen der relativen Wahrscheinlichkeit (negativer Logarithmus) double relFreqE = log(singlecount_e) - log(paircount); double unigram = log(eCount) - log(singlecount_e); std::cout << relFreqF << " " << relFreqE << " # " << source_phrase << "# " << target_phrase << "# " << singlecount_f << " "<< singlecount_e << " # " << source_to_target << " " << target_to_source << " # " << unigram << "\n"; //Ausgabe } } } } return 0; }
bool CarGraphics::Load( const PTree & cfg, const std::string & carpath, const std::string & /*carname*/, const std::string & carwheel, const std::string & carpaint, const Vec3 & carcolor, const int anisotropy, const float camerabounce, ContentManager & content, std::ostream & error_output) { assert(!loaded); // init drawable load functor LoadDrawable loadDrawable(carpath, anisotropy, content, models, textures, error_output); // load body first const PTree * cfg_body; std::string meshname; std::vector<std::string> texname; if (!cfg.get("body", cfg_body, error_output)) return false; if (!cfg_body->get("mesh", meshname, error_output)) return false; if (!cfg_body->get("texture", texname, error_output)) return false; if (carpaint != "default") texname[0] = carpaint; if (!loadDrawable(meshname, texname, *cfg_body, topnode, &bodynode)) return false; // load wheels const PTree * cfg_wheels; if (!cfg.get("wheel", cfg_wheels, error_output)) return false; std::shared_ptr<PTree> sel_wheel; if (carwheel != "default" && !content.load(sel_wheel, carpath, carwheel)) return false; for (PTree::const_iterator i = cfg_wheels->begin(); i != cfg_wheels->end(); ++i) { const PTree * cfg_wheel = &i->second; // override default wheel with selected, not very efficient, fixme PTree opt_wheel; if (sel_wheel.get()) { opt_wheel.set(*sel_wheel); opt_wheel.merge(*cfg_wheel); cfg_wheel = &opt_wheel; } if (!LoadWheel(*cfg_wheel, loadDrawable, topnode, error_output)) { error_output << "Failed to load wheels." << std::endl; return false; } } // load drawables LoadBody loadBody(topnode, bodynode, loadDrawable); for (PTree::const_iterator i = cfg.begin(); i != cfg.end(); ++i) { if (i->first != "body" && i->first != "steering" && i->first != "light-brake" && i->first != "light-reverse") { loadBody(i->second); } } // load steering wheel const PTree * cfg_steer; if (cfg.get("steering", cfg_steer)) { SceneNode & bodynoderef = topnode.GetNode(bodynode); if (!loadDrawable(*cfg_steer, bodynoderef, &steernode, 0)) { error_output << "Failed to load steering wheel." << std::endl; return false; } cfg_steer->get("max-angle", steer_angle_max); steer_angle_max = steer_angle_max / 180.0 * M_PI; SceneNode & steernoderef = bodynoderef.GetNode(steernode); steer_orientation = steernoderef.GetTransform().GetRotation(); steer_rotation = steer_orientation; } // load brake/reverse light point light sources (optional) int i = 0; std::string istr = "0"; const PTree * cfg_light; while (cfg.get("light-brake-"+istr, cfg_light)) { if (!LoadLight(*cfg_light, content, error_output)) { error_output << "Failed to load lights." << std::endl; return false; } std::ostringstream sstr; sstr << ++i; istr = sstr.str(); } i = 0; istr = "0"; while (cfg.get("light-reverse-"+istr, cfg_light)) { if (!LoadLight(*cfg_light, content, error_output)) { error_output << "Failed to load lights." << std::endl; return false; } std::ostringstream sstr; sstr << ++i; istr = sstr.str(); } // load car brake/reverse graphics (optional) if (cfg.get("light-brake", cfg_light)) { SceneNode & bodynoderef = topnode.GetNode(bodynode); if (!loadDrawable(*cfg_light, bodynoderef, 0, &brakelights)) { error_output << "Failed to load lights." << std::endl; return false; } } if (cfg.get("light-reverse", cfg_light)) { SceneNode & bodynoderef = topnode.GetNode(bodynode); if (!loadDrawable(*cfg_light, bodynoderef, 0, &reverselights)) { error_output << "Failed to load lights." << std::endl; return false; } } if (!LoadCameras(cfg, camerabounce, error_output)) { return false; } SetColor(carcolor[0], carcolor[1], carcolor[2]); loaded = true; return true; }