From 7d542e1daf09caadf6d3e36c4b09bdf94952c5a1 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 5 Apr 2023 15:13:00 +0200 Subject: Brush up the container code snippets - Bring iterator loops into a consistent form using auto and creating and end variable, use cbegin()/cend() where suitable - Use (std::)endl instead of Qt::endl for iostreams - Fix removed container conversion API (QList::fromSet, QSet::toList()) - Use range-based for instead of foreach - Use initializer lists - Use qPrintable(QString) for output to std::ostream - Use qsizetype - Remove some unused snippets Complements f6b137bdc43d4021cbbe602759dbcced2e04d638. Pick-to: 6.5 Change-Id: I8a167099cdb224f45b984fa834d46269144a7ef0 Reviewed-by: Christian Ehrlicher Reviewed-by: Ahmad Samir Reviewed-by: Marc Mutz --- src/corelib/doc/snippets/code/doc_src_qset.cpp | 26 +++++------ .../doc/snippets/code/src_corelib_io_qsettings.cpp | 6 +-- .../code/src_corelib_serialization_qcborstream.cpp | 2 +- .../snippets/code/src_corelib_text_qbytearray.cpp | 6 +-- .../snippets/code/src_corelib_thread_qfuture.cpp | 2 +- .../doc/snippets/code/src_corelib_tools_qhash.cpp | 52 ++++++++++------------ .../doc/snippets/code/src_corelib_tools_qlist.cpp | 16 +++---- .../doc/snippets/code/src_corelib_tools_qmap.cpp | 36 +++++++-------- .../snippets/code/src_corelib_tools_qmultimap.cpp | 44 +++++++++--------- .../doc/snippets/code/src_corelib_tools_qqueue.cpp | 2 +- src/corelib/doc/snippets/qstringlist/main.cpp | 2 +- src/corelib/tools/qhash.cpp | 2 +- src/corelib/tools/qmap.qdoc | 2 +- src/corelib/tools/qmultimap.qdoc | 2 +- src/corelib/tools/qset.qdoc | 2 +- 15 files changed, 88 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/src/corelib/doc/snippets/code/doc_src_qset.cpp b/src/corelib/doc/snippets/code/doc_src_qset.cpp index 37f7ced3b0..f73e593249 100644 --- a/src/corelib/doc/snippets/code/doc_src_qset.cpp +++ b/src/corelib/doc/snippets/code/doc_src_qset.cpp @@ -34,18 +34,15 @@ while (i.hasNext()) { //! [5] -QSet::const_iterator i = set.constBegin(); -while (i != set.constEnd()) { +for (auto i = set.cbegin(), end = set.cend(); != end; ++i) qDebug() << *i; - ++i; -} //! [5] //! [6] QSet set; ... -foreach (const QString &value, set) +for (const auto &value : set) qDebug() << value; //! [6] @@ -59,20 +56,18 @@ for (int i = 0; i < 20000; ++i) //! [8] -QSet set; -set << "January" << "February" << ... << "December"; +QSet set = {"January", "February", ... "December"} -QSet::iterator i; -for (i = set.begin(); i != set.end(); ++i) +// i is a QSet::iterator +for (auto i = set.begin(), end = set.end(); i != end; ++i) qDebug() << *i; //! [8] //! [9] -QSet set; -set << "January" << "February" << ... << "December"; +QSet set = {"January", "February", ... "December"}; -QSet::iterator i = set.begin(); +auto i = set.begin(); while (i != set.end()) { if ((*i).startsWith('J')) { i = set.erase(i); @@ -94,11 +89,10 @@ if (it != set.end()) //! [11] -QSet set; -set << "January" << "February" << ... << "December"; +QSet set = {"January", "February", ... "December"}; -QSet::const_iterator i; -for (i = set.begin(); i != set.end(); ++i) +// i is QSet::const_iterator +for (auto i = set.cbegin(), end = set.cend(); i != end; ++i) qDebug() << *i; //! [11] diff --git a/src/corelib/doc/snippets/code/src_corelib_io_qsettings.cpp b/src/corelib/doc/snippets/code/src_corelib_io_qsettings.cpp index 92e40c3667..5774add2a7 100644 --- a/src/corelib/doc/snippets/code/src_corelib_io_qsettings.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_io_qsettings.cpp @@ -138,10 +138,10 @@ QList logins; QSettings settings; settings.beginWriteArray("logins"); -for (int i = 0; i < logins.size(); ++i) { +for (qsizetype i = 0; i < logins.size(); ++i) { settings.setArrayIndex(i); - settings.setValue("userName", list.at(i).userName); - settings.setValue("password", list.at(i).password); + settings.setValue("userName", logins.at(i).userName); + settings.setValue("password", logins.at(i).password); } settings.endArray(); //! [16] diff --git a/src/corelib/doc/snippets/code/src_corelib_serialization_qcborstream.cpp b/src/corelib/doc/snippets/code/src_corelib_serialization_qcborstream.cpp index 90f89d8bd1..51b6bbbf46 100644 --- a/src/corelib/doc/snippets/code/src_corelib_serialization_qcborstream.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_serialization_qcborstream.cpp @@ -198,7 +198,7 @@ using namespace Qt::StringLiterals; void appendMap(QCborStreamWriter &writer, const QMap &map) { writer.startMap(map.size()); - for (auto it = map.begin(); it != map.end(); ++it) { + for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) { writer.append(it.key()); writer.append(it.value()); } diff --git a/src/corelib/doc/snippets/code/src_corelib_text_qbytearray.cpp b/src/corelib/doc/snippets/code/src_corelib_text_qbytearray.cpp index 308fe0a5ac..65e7234608 100644 --- a/src/corelib/doc/snippets/code/src_corelib_text_qbytearray.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_text_qbytearray.cpp @@ -24,7 +24,7 @@ ba[4] = 0xca; //! [2] for (qsizetype i = 0; i < ba.size(); ++i) { if (ba.at(i) >= 'a' && ba.at(i) <= 'f') - cout << "Found character in range [a-f]" << Qt::endl; + cout << "Found character in range [a-f]" << endl; } //! [2] @@ -41,7 +41,7 @@ x.replace(5, 3, "&"); // x == "rock & roll" QByteArray ba("We must be bold, very bold"); qsizetype j = 0; while ((j = ba.indexOf("", j)) != -1) { - cout << "Found tag at index position " << j << Qt::endl; + cout << "Found tag at index position " << j << endl; ++j; } //! [4] @@ -79,7 +79,7 @@ QByteArray("abc").isEmpty(); // returns false QByteArray ba("Hello world"); char *data = ba.data(); while (*data) { - cout << "[" << *data << "]" << Qt::endl; + cout << "[" << *data << "]" << endl; ++data; } //! [8] diff --git a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp index ea104ec1a9..bbd61d6dd3 100644 --- a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp @@ -6,7 +6,7 @@ QFuture future = ...; QFuture::const_iterator i; for (i = future.constBegin(); i != future.constEnd(); ++i) - cout << *i << Qt::endl; + cout << qPrintable(*i) << endl; //! [0] diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp index 6edfbd0e28..a44c5eb733 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp @@ -42,7 +42,7 @@ QHash hash; ... for (int i = 0; i < 1000; ++i) { if (hash[i] == okButton) - cout << "Found button at index " << i << Qt::endl; + cout << "Found button at index " << i << endl; } //! [6] @@ -51,17 +51,14 @@ for (int i = 0; i < 1000; ++i) { QHashIterator i(hash); while (i.hasNext()) { i.next(); - cout << i.key() << ": " << i.value() << Qt::endl; + cout << qPrintable(i.key()) << ": " << i.value() << endl; } //! [7] //! [8] -QHash::const_iterator i = hash.cbegin(); -while (i != hash.cend()) { - cout << i.key() << ": " << i.value() << Qt::endl; - ++i; -} +for (auto i = map.cbegin(), end = map.cend(); != end; ++i) + cout << qPrintable(i.key()) << ": " << i.value() << endl; //! [8] @@ -75,8 +72,8 @@ hash.insert("plenty", 2000); //! [12] QHash hash; ... -foreach (int value, hash) - cout << value << Qt::endl; +for (int value : std::as_const(hash)) + cout << value << endl; //! [12] @@ -138,7 +135,7 @@ QHash hash; ... QHash::const_iterator i = hash.find("HDR"); while (i != hash.end() && i.key() == "HDR") { - cout << i.value() << Qt::endl; + cout << i.value() << endl; ++i; } //! [16] @@ -151,15 +148,13 @@ hash.insert("February", 2); ... hash.insert("December", 12); -QHash::iterator i; -for (i = hash.begin(); i != hash.end(); ++i) - cout << i.key() << ": " << i.value() << Qt::endl; +for (auto i = hash.cbegin(), end = hash.cend(); i != end; ++i) + cout << qPrintable(key()) << ": " << i.value() << endl; //! [17] //! [18] -QHash::iterator i; -for (i = hash.begin(); i != hash.end(); ++i) +for (auto i = hash.begin(), end = hash.end(); i != end; ++i) i.value() += 2; //! [18] @@ -181,9 +176,8 @@ hash.insert("February", 2); ... hash.insert("December", 12); -QHash::const_iterator i; -for (i = hash.cbegin(); i != hash.cend(); ++i) - cout << i.key() << ": " << i.value() << Qt::endl; +for (auto i = hash.cbegin(), end = hash.cend(); i != end; ++i) + cout << qPrintable(i.key()) << ": " << i.value() << endl; //! [23] @@ -204,24 +198,24 @@ hash3 = hash1 + hash2; //! [25] QList values = hash.values("plenty"); -for (int i = 0; i < values.size(); ++i) - cout << values.at(i) << Qt::endl; +for (auto i : std::as_const(values)) + cout << i << endl; //! [25] //! [26] -QMultiHash::iterator i = hash.find("plenty"); -while (i != hash.end() && i.key() == "plenty") { - cout << i.value() << Qt::endl; +auto i = hash.constFind("plenty"); +while (i != hash.cend() && i.key() == "plenty") { + cout << i.value() << endl; ++i; } //! [26] //! [27] -for (QHash::const_iterator it = hash.cbegin(), end = hash.cend(); it != end; ++it) { - cout << "The key: " << it.key() << Qt::endl - cout << "The value: " << it.value() << Qt::endl; - cout << "Also the value: " << (*it) << Qt::endl; +for (auto it = hash.cbegin(), end = hash.cend(); it != end; ++it) { + cout << "The key: " << it.key() << endl; + cout << "The value: " << qPrintable(it.value()) << endl; + cout << "Also the value: " << qPrintable(*it) << endl; } //! [27] @@ -294,7 +288,7 @@ hash.insert("February", 2); hash.insert("December", 12); for (auto [key, value] : hash.asKeyValueRange()) { - cout << key << ": " << value << Qt::endl; + cout << qPrintable(key) << ": " << value << endl; --value; // convert to JS month indexing } //! [34] @@ -307,7 +301,7 @@ hash.insert("February", 2); hash.insert("December", 12); for (auto [key, value] : hash.asKeyValueRange()) { - cout << key << ": " << value << Qt::endl; + cout << qPrintable(key) << ": " << value << endl; --value; // convert to JS month indexing } //! [35] diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qlist.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qlist.cpp index 8957d43476..499e8fe480 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qlist.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qlist.cpp @@ -26,7 +26,7 @@ if (list[0] == "Liz") //! [4] for (qsizetype i = 0; i < list.size(); ++i) { if (list.at(i) == "Alfonso") - cout << "Found Alfonso at position " << i << Qt::endl; + cout << "Found Alfonso at position " << i << endl; } //! [4] @@ -34,7 +34,7 @@ for (qsizetype i = 0; i < list.size(); ++i) { //! [5] qsizetype i = list.indexOf("Harumi"); if (i != -1) - cout << "First occurrence of Harumi is at position " << i << Qt::endl; + cout << "First occurrence of Harumi is at position " << i << endl; //! [5] @@ -101,16 +101,14 @@ list.prepend("three"); //! [9] -QList list; -list << "alpha" << "beta" << "delta"; +QList list = {"alpha", "beta", "delta"}; list.insert(2, "gamma"); // list: ["alpha", "beta", "gamma", "delta"] //! [9] //! [10] -QList list; -list << 2.718 << 1.442 << 0.4342; +QList list = {2.718, 1.442, 0.4342}; list.insert(1, 3, 9.9); // list: [2.718, 9.9, 9.9, 9.9, 1.442, 0.4342] //! [10] @@ -127,8 +125,7 @@ list.fill("oh", 5); //! [12] -QList list; -list << "A" << "B" << "C" << "B" << "A"; +QList list{"A", "B", "C", "B", "A"}; list.indexOf("B"); // returns 1 list.indexOf("B", 1); // returns 1 list.indexOf("B", 2); // returns 3 @@ -137,8 +134,7 @@ list.indexOf("X"); // returns -1 //! [13] -QList list; -list << "A" << "B" << "C" << "B" << "A"; +QList list = {"A", "B", "C", "B", "A"}; list.lastIndexOf("B"); // returns 3 list.lastIndexOf("B", 3); // returns 3 list.lastIndexOf("B", 2); // returns 1 diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qmap.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qmap.cpp index 462eb28a1e..fd400c6411 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qmap.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qmap.cpp @@ -42,7 +42,7 @@ QMap map; ... for (int i = 0; i < 1000; ++i) { if (map[i] == okButton) - cout << "Found button at index " << i << Qt::endl; + cout << "Found button at index " << i << endl; } //! [6] @@ -51,17 +51,14 @@ for (int i = 0; i < 1000; ++i) { QMapIterator i(map); while (i.hasNext()) { i.next(); - cout << i.key() << ": " << i.value() << Qt::endl; + cout << qPrintable(i.key()) << ": " << i.value() << endl; } //! [7] //! [8] -QMap::const_iterator i = map.cbegin(); -while (i != map.cend()) { - cout << i.key() << ": " << i.value() << Qt::endl; - ++i; -} +for (auto i = map.cbegin(), end = map.cend(); != end; ++i) + cout << qPrintable(i.key()) << ": " << i.value() << endl; //! [8] @@ -75,8 +72,8 @@ map.insert("plenty", 2000); //! [12] QMap map; ... -foreach (int value, map) - cout << value << Qt::endl; +for (int value : std::as_const(map)) + cout << value << endl; //! [12] @@ -128,15 +125,13 @@ map.insert("February", 2); ... map.insert("December", 12); -QMap::iterator i; -for (i = map.begin(); i != map.end(); ++i) - cout << i.key() << ": " << i.value() << Qt::endl; +for (auto i = map.cbegin(), end = map.cend(); i != end; ++i) + cout << qPrintable(i.key()) << ": " << i.value() << endl; //! [18] //! [19] -QMap::iterator i; -for (i = map.begin(); i != map.end(); ++i) +for (auto i = map.begin(), end = map.end(); i != end; ++i) i.value() += 2; //! [19] @@ -171,17 +166,16 @@ map.insert("February", 2); ... map.insert("December", 12); -QMap::const_iterator i; -for (i = map.cbegin(); i != map.cend(); ++i) - cout << i.key() << ": " << i.value() << Qt::endl; +for (auto i = map.cbegin(), end = map.cend(); i != end; ++i) + cout << qPrintable(i.key()) << ": " << i.value() << endl; //! [24] //! [keyiterator1] for (QMap::const_iterator it = map.cbegin(), end = map.cend(); it != end; ++it) { - cout << "The key: " << it.key() << Qt::endl - cout << "The value: " << it.value() << Qt::endl; - cout << "Also the value: " << (*it) << Qt::endl; + cout << "The key: " << it.key() << endl; + cout << "The value: " << qPrintable(it.value()) << endl; + cout << "Also the value: " << qPrintable(*it) << endl; } //! [keyiterator1] @@ -204,7 +198,7 @@ map.insert("February", 2); map.insert("December", 12); for (auto [key, value] : map.asKeyValueRange()) { - cout << key << ": " << value << Qt::endl; + cout << qPrintable(key) << ": " << value << endl; --value; // convert to JS month indexing } //! [28] diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qmultimap.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qmultimap.cpp index 55bf77813e..42ec46585b 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qmultimap.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qmultimap.cpp @@ -19,8 +19,8 @@ multimap.insert("c", -5); int num2 = multimap.value("a"); // 1 int num3 = multimap.value("thirteen"); // not found; 0 int num3 = 0; -auto it = multimap.value("b"); -if (it != multimap.end()) { +auto it = multimap.constFind("b"); +if (it != multimap.cend()) { num3 = it.value(); } //! [3] @@ -47,17 +47,14 @@ int timeout = multimap.value("TIMEOUT", 30); QMultiMapIterator i(multimap); while (i.hasNext()) { i.next(); - cout << i.key() << ": " << i.value() << Qt::endl; + cout << qPrintable(i.key()) << ": " << i.value() << endl; } //! [7] //! [8] -auto i = multimap.cbegin(); -while (i != multimap.cend()) { - cout << i.key() << ": " << i.value() << Qt::endl; - ++i; -} +for (auto i = multimap.cbegin(), end = multimap.cend(); i != end; ++i) + cout << qPrintable(i.key()) << ": " << i.value() << endl; //! [8] @@ -70,22 +67,22 @@ multimap.insert("plenty", 2000); //! [10] QList values = multimap.values("plenty"); -for (int i = 0; i < values.size(); ++i) - cout << values.at(i) << Qt::endl; +for (auto i : std::as_const(values)) + cout << i << endl; //! [10] //! [11] -QMultiMap::iterator i = multimap.find("plenty"); +auto i = multimap.find("plenty"); while (i != map.end() && i.key() == "plenty") { - cout << i.value() << Qt::endl; + cout << i.value() << endl; ++i; } // better: auto [i, end] = multimap.equal_range("plenty"); while (i != end) { - cout << i.value() << Qt::endl; + cout << i.value() << endl; ++i; } //! [11] @@ -94,8 +91,8 @@ while (i != end) { //! [12] QMap multimap; ... -foreach (int value, multimap) - cout << value << Qt::endl; +for (int value : std::as_const(multimap)) + cout << value << endl; //! [12] @@ -149,7 +146,7 @@ QMap multimap; QMap::const_iterator i = multimap.lowerBound("HDR"); QMap::const_iterator upperBound = multimap.upperBound("HDR"); while (i != upperBound) { - cout << i.value() << Qt::endl; + cout << i.value() << endl; ++i; } //! [16] @@ -207,9 +204,8 @@ multimap.insert("February", 2); ... multimap.insert("December", 12); -QMultiMap::const_iterator i; -for (i = multimap.cbegin(); i != multimap.cend(); ++i) - cout << i.key() << ": " << i.value() << Qt::endl; +for (auto i = multimap.cbegin(), end = multimap.cend(); i != end; ++i) + cout << qPrintable(i.key()) << ": " << i.value() << endl; //! [24] @@ -228,10 +224,10 @@ map3 = map1 + map2; //! [25] //! [keyiterator1] -for (QMultiMap::const_iterator it = multimap.cbegin(), end = multimap.cend(); it != end; ++it) { - cout << "The key: " << it.key() << Qt::endl - cout << "The value: " << it.value() << Qt::endl; - cout << "Also the value: " << (*it) << Qt::endl; +for (auto it = multimap.cbegin(), end = multimap.cend(); it != end; ++it) { + cout << "The key: " << it.key() << endl + cout << "The value: " << qPrintable(it.value()) << endl; + cout << "Also the value: " << qPrintable(*it) << endl; } //! [keyiterator1] @@ -254,7 +250,7 @@ map.insert("February", 2); map.insert("December", 12); for (auto [key, value] : map.asKeyValueRange()) { - cout << key << ": " << value << Qt::endl; + cout << qPrintable(key) << ": " << value << endl; --value; // convert to JS month indexing } //! [26] diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qqueue.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qqueue.cpp index 81b8eb4dbd..c59ec1060a 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qqueue.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qqueue.cpp @@ -7,5 +7,5 @@ queue.enqueue(1); queue.enqueue(2); queue.enqueue(3); while (!queue.isEmpty()) - cout << queue.dequeue() << Qt::endl; + cout << queue.dequeue() << endl; //! [0] diff --git a/src/corelib/doc/snippets/qstringlist/main.cpp b/src/corelib/doc/snippets/qstringlist/main.cpp index d2175aafb5..e9567fb107 100644 --- a/src/corelib/doc/snippets/qstringlist/main.cpp +++ b/src/corelib/doc/snippets/qstringlist/main.cpp @@ -66,7 +66,7 @@ Widget::Widget(QWidget *parent) result.clear(); //! [12] - foreach (const QString &str, list) { + for (const auto &str : std::as_const(list)) { if (str.contains("Bill")) result += str; } diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 2f025847eb..f90c351118 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -1640,7 +1640,7 @@ size_t qHash(long double key, size_t seed) noexcept hash table, use \l{QMultiHash}. If you only need to extract the values from a hash (not the keys), - you can also use \l{foreach}: + you can also use range-based for: \snippet code/src_corelib_tools_qhash.cpp 12 diff --git a/src/corelib/tools/qmap.qdoc b/src/corelib/tools/qmap.qdoc index a5c5c59b4e..bbf20ef02b 100644 --- a/src/corelib/tools/qmap.qdoc +++ b/src/corelib/tools/qmap.qdoc @@ -97,7 +97,7 @@ QMultiMap. If you only need to extract the values from a map (not the keys), - you can also use \l{foreach}: + you can also use range-based for: \snippet code/src_corelib_tools_qmap.cpp 12 diff --git a/src/corelib/tools/qmultimap.qdoc b/src/corelib/tools/qmultimap.qdoc index 6e98ffcdba..5bc4af0940 100644 --- a/src/corelib/tools/qmultimap.qdoc +++ b/src/corelib/tools/qmultimap.qdoc @@ -92,7 +92,7 @@ \snippet code/src_corelib_tools_qmultimap.cpp 11 If you only need to extract the values from a map (not the keys), - you can also use \l{foreach}: + you can also use range-based for: \snippet code/src_corelib_tools_qmultimap.cpp 12 diff --git a/src/corelib/tools/qset.qdoc b/src/corelib/tools/qset.qdoc index a8d61a6d92..31b431e43f 100644 --- a/src/corelib/tools/qset.qdoc +++ b/src/corelib/tools/qset.qdoc @@ -46,7 +46,7 @@ QSet is unordered, so an iterator's sequence cannot be assumed to be predictable. If ordering by key is required, use a QMap. - To navigate through a QSet, you can also use \l{foreach}: + To navigate through a QSet, you can also use range-based for: \snippet code/doc_src_qset.cpp 6 -- cgit v1.2.3