dynamic_vector_test.cc revision 83be94436ec4d35296e00da877c7198f67b6af44
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "gtest/gtest.h" 18 19#include "chre/util/array.h" 20#include "chre/util/dynamic_vector.h" 21 22using chre::DynamicVector; 23 24namespace { 25constexpr int kMaxTestCapacity = 10; 26int gDestructorCount[kMaxTestCapacity]; 27 28class Dummy { 29 public: 30 ~Dummy() { 31 if (mValue >= 0) { 32 gDestructorCount[mValue]++; 33 } 34 }; 35 void setValue(int value) { 36 mValue = value; 37 } 38 39 private: 40 int mValue = -1; 41}; 42 43void resetDestructorCounts() { 44 for (size_t i = 0; i < ARRAY_SIZE(gDestructorCount); i++) { 45 gDestructorCount[i] = 0; 46 } 47} 48} 49 50TEST(DynamicVector, EmptyByDefault) { 51 DynamicVector<int> vector; 52 ASSERT_EQ(vector.data(), nullptr); 53 ASSERT_EQ(vector.size(), 0); 54 ASSERT_EQ(vector.capacity(), 0); 55} 56 57TEST(DynamicVector, PushBackAndRead) { 58 DynamicVector<int> vector; 59 ASSERT_TRUE(vector.push_back(0x1337)); 60 ASSERT_EQ(vector[0], 0x1337); 61 ASSERT_EQ(vector.data()[0], 0x1337); 62} 63 64TEST(DynamicVector, PushBackReserveAndRead) { 65 DynamicVector<int> vector; 66 ASSERT_TRUE(vector.push_back(0x1337)); 67 ASSERT_TRUE(vector.push_back(0xface)); 68 ASSERT_TRUE(vector.reserve(4)); 69 ASSERT_EQ(vector[0], 0x1337); 70 ASSERT_EQ(vector.data()[0], 0x1337); 71 ASSERT_EQ(vector[1], 0xface); 72 ASSERT_EQ(vector.data()[1], 0xface); 73} 74 75class MovableButNonCopyable : public chre::NonCopyable { 76 public: 77 MovableButNonCopyable(int value) : mValue(value) {} 78 79 MovableButNonCopyable(MovableButNonCopyable&& other) { 80 mValue = other.mValue; 81 } 82 83 MovableButNonCopyable& operator=(MovableButNonCopyable&& other) { 84 mValue = other.mValue; 85 return *this; 86 } 87 88 int getValue() const { 89 return mValue; 90 } 91 92 private: 93 int mValue; 94}; 95 96TEST(DynamicVector, PushBackReserveAndReadMovableButNonCopyable) { 97 DynamicVector<MovableButNonCopyable> vector; 98 ASSERT_TRUE(vector.emplace_back(0x1337)); 99 ASSERT_TRUE(vector.emplace_back(0xface)); 100 ASSERT_TRUE(vector.reserve(4)); 101 EXPECT_EQ(vector[0].getValue(), 0x1337); 102 EXPECT_EQ(vector.data()[0].getValue(), 0x1337); 103 EXPECT_EQ(vector[1].getValue(), 0xface); 104 EXPECT_EQ(vector.data()[1].getValue(), 0xface); 105} 106 107class CopyableButNonMovable { 108 public: 109 CopyableButNonMovable(int value) : mValue(value) {} 110 111 CopyableButNonMovable(const CopyableButNonMovable& other) { 112 mValue = other.mValue; 113 } 114 115 CopyableButNonMovable(CopyableButNonMovable&& other) = delete; 116 117 CopyableButNonMovable& operator=(const CopyableButNonMovable& other) { 118 mValue = other.mValue; 119 return *this; 120 } 121 122 CopyableButNonMovable& operator=(CopyableButNonMovable&& other) = delete; 123 124 int getValue() const { 125 return mValue; 126 } 127 128 private: 129 int mValue; 130}; 131 132TEST(DynamicVector, PushBackReserveAndReadCopyableButNonMovable) { 133 DynamicVector<CopyableButNonMovable> vector; 134 ASSERT_TRUE(vector.emplace_back(0xcafe)); 135 ASSERT_TRUE(vector.emplace_back(0xface)); 136 ASSERT_TRUE(vector.reserve(4)); 137 EXPECT_EQ(vector[0].getValue(), 0xcafe); 138 EXPECT_EQ(vector.data()[0].getValue(), 0xcafe); 139 EXPECT_EQ(vector[1].getValue(), 0xface); 140 EXPECT_EQ(vector.data()[1].getValue(), 0xface); 141} 142 143class MovableAndCopyable { 144 public: 145 MovableAndCopyable(int value) : mValue(value) {} 146 147 MovableAndCopyable(const MovableAndCopyable& other) { 148 mValue = other.mValue; 149 } 150 151 MovableAndCopyable(MovableAndCopyable&& other) { 152 mValue = other.mValue; 153 } 154 155 MovableAndCopyable& operator=(const MovableAndCopyable& other) { 156 mValue = other.mValue; 157 return *this; 158 } 159 160 MovableAndCopyable& operator=(MovableAndCopyable&& other) { 161 // The move operation multiplies the value by 2 so that we can see that the 162 // move assignment operator was used. 163 mValue = other.mValue * 2; 164 return *this; 165 } 166 167 int getValue() const { 168 return mValue; 169 } 170 171 private: 172 int mValue; 173}; 174 175TEST(DynamicVector, PushBackReserveAndReadMovableAndCopyable) { 176 // Ensure that preference is given to std::move. 177 DynamicVector<MovableAndCopyable> vector; 178 179 // Reserve enough space for the first two elements. 180 ASSERT_TRUE(vector.reserve(2)); 181 ASSERT_TRUE(vector.emplace_back(1000)); 182 ASSERT_TRUE(vector.emplace_back(2000)); 183 184 // Reserve more than enough space causing a move to be required. 185 ASSERT_TRUE(vector.reserve(4)); 186 187 // Move on this type results in a multiplication by 2. Verify that all 188 // elements have been multiplied by 2. 189 EXPECT_EQ(vector[0].getValue(), 2000); 190 EXPECT_EQ(vector.data()[0].getValue(), 2000); 191 EXPECT_EQ(vector[1].getValue(), 4000); 192 EXPECT_EQ(vector.data()[1].getValue(), 4000); 193} 194 195/** 196 * A simple test helper object to count number of construction and destructions. 197 */ 198class Foo { 199 public: 200 /** 201 * Construct an object storing a simple integer. Increment the number of 202 * objects that have been constructed of this type. 203 */ 204 Foo(int value) : value(value) { 205 sConstructedCounter++; 206 } 207 208 /** 209 * Tear down the object, decrementing the number of objects that have been 210 * constructed of this type. 211 */ 212 ~Foo() { 213 sConstructedCounter--; 214 } 215 216 //! The number of objects of this type that have been constructed. 217 static size_t sConstructedCounter; 218 219 //! The value stored in the object to verify the contents of this object after 220 //! construction. 221 int value; 222}; 223 224//! Storage for the Foo reference counter. 225size_t Foo::sConstructedCounter = 0; 226 227TEST(DynamicVector, EmplaceBackAndDestruct) { 228 { 229 DynamicVector<Foo> vector; 230 ASSERT_TRUE(vector.emplace_back(1000)); 231 ASSERT_TRUE(vector.emplace_back(2000)); 232 ASSERT_TRUE(vector.emplace_back(3000)); 233 ASSERT_TRUE(vector.emplace_back(4000)); 234 235 ASSERT_EQ(vector[0].value, 1000); 236 ASSERT_EQ(vector.data()[0].value, 1000); 237 ASSERT_EQ(vector[1].value, 2000); 238 ASSERT_EQ(vector.data()[1].value, 2000); 239 ASSERT_EQ(vector[2].value, 3000); 240 ASSERT_EQ(vector.data()[2].value, 3000); 241 ASSERT_EQ(vector[3].value, 4000); 242 ASSERT_EQ(vector.data()[3].value, 4000); 243 244 ASSERT_EQ(Foo::sConstructedCounter, 4); 245 } 246 247 ASSERT_EQ(Foo::sConstructedCounter, 0); 248} 249 250TEST(DynamicVector, InsertEmpty) { 251 DynamicVector<int> vector; 252 EXPECT_CHRE_ASSERT(EXPECT_FALSE(vector.insert(1, 0x1337))); 253 ASSERT_TRUE(vector.insert(0, 0x1337)); 254 EXPECT_EQ(vector[0], 0x1337); 255 EXPECT_EQ(vector.data()[0], 0x1337); 256} 257 258TEST(DynamicVector, PushBackInsertInMiddleAndRead) { 259 DynamicVector<int> vector; 260 ASSERT_TRUE(vector.push_back(0x1337)); 261 ASSERT_TRUE(vector.push_back(0xface)); 262 ASSERT_TRUE(vector.push_back(0xcafe)); 263 ASSERT_TRUE(vector.insert(1, 0xbeef)); 264 265 ASSERT_EQ(vector[0], 0x1337); 266 ASSERT_EQ(vector.data()[0], 0x1337); 267 ASSERT_EQ(vector[1], 0xbeef); 268 ASSERT_EQ(vector.data()[1], 0xbeef); 269 ASSERT_EQ(vector[2], 0xface); 270 ASSERT_EQ(vector.data()[2], 0xface); 271 ASSERT_EQ(vector[3], 0xcafe); 272 ASSERT_EQ(vector.data()[3], 0xcafe); 273} 274 275TEST(DynamicVector, PushBackAndErase) { 276 DynamicVector<int> vector; 277 ASSERT_TRUE(vector.push_back(0x1337)); 278 ASSERT_TRUE(vector.push_back(0xcafe)); 279 ASSERT_TRUE(vector.push_back(0xbeef)); 280 ASSERT_TRUE(vector.push_back(0xface)); 281 282 vector.erase(1); 283 284 ASSERT_EQ(vector[0], 0x1337); 285 ASSERT_EQ(vector.data()[0], 0x1337); 286 ASSERT_EQ(vector[1], 0xbeef); 287 ASSERT_EQ(vector.data()[1], 0xbeef); 288 ASSERT_EQ(vector[2], 0xface); 289 ASSERT_EQ(vector.data()[2], 0xface); 290 ASSERT_EQ(vector.size(), 3); 291} 292 293TEST(DynamicVector, FindEmpty) { 294 DynamicVector<int> vector; 295 ASSERT_EQ(vector.find(0), 0); 296} 297 298TEST(DynamicVector, FindWithElements) { 299 DynamicVector<int> vector; 300 ASSERT_TRUE(vector.push_back(0x1337)); 301 ASSERT_TRUE(vector.push_back(0xcafe)); 302 ASSERT_TRUE(vector.push_back(0xbeef)); 303 304 ASSERT_EQ(vector.find(0x1337), 0); 305 ASSERT_EQ(vector.find(0xcafe), 1); 306 ASSERT_EQ(vector.find(0xbeef), 2); 307 ASSERT_EQ(vector.find(1000), 3); 308} 309 310TEST(DynamicVector, EraseDestructorCalled) { 311 resetDestructorCounts(); 312 313 DynamicVector<Dummy> vector; 314 for (size_t i = 0; i < 4; ++i) { 315 vector.push_back(Dummy()); 316 vector[i].setValue(i); 317 } 318 319 // last item before erase is '3'. 320 vector.erase(1); 321 EXPECT_EQ(0, gDestructorCount[0]); 322 EXPECT_EQ(0, gDestructorCount[1]); 323 EXPECT_EQ(0, gDestructorCount[2]); 324 EXPECT_EQ(1, gDestructorCount[3]); 325 326 // last item before erase is still '3'. 327 vector.erase(2); 328 EXPECT_EQ(0, gDestructorCount[0]); 329 EXPECT_EQ(0, gDestructorCount[1]); 330 EXPECT_EQ(0, gDestructorCount[2]); 331 EXPECT_EQ(2, gDestructorCount[3]); 332 333 // last item before erase is now '2'. 334 vector.erase(0); 335 EXPECT_EQ(0, gDestructorCount[0]); 336 EXPECT_EQ(0, gDestructorCount[1]); 337 EXPECT_EQ(1, gDestructorCount[2]); 338 EXPECT_EQ(2, gDestructorCount[3]); 339} 340 341TEST(DynamicVector, Clear) { 342 resetDestructorCounts(); 343 344 DynamicVector<Dummy> vector; 345 for (size_t i = 0; i < 4; ++i) { 346 vector.push_back(Dummy()); 347 vector[i].setValue(i); 348 } 349 350 vector.clear(); 351 EXPECT_EQ(vector.size(), 0); 352 EXPECT_EQ(vector.capacity(), 4); 353 354 for (size_t i = 0; i < 4; ++i) { 355 EXPECT_EQ(gDestructorCount[i], 1); 356 } 357} 358 359// Make sure that a vector wrapping an array doesn't call the destructor when 360// the vector is destructed 361TEST(DynamicVector, WrapDoesntCallDestructor) { 362 resetDestructorCounts(); 363 364 Dummy array[4]; 365 for (size_t i = 0; i < 4; ++i) { 366 array[i].setValue(i); 367 } 368 369 { 370 DynamicVector<Dummy> vector; 371 vector.wrap(array, ARRAY_SIZE(array)); 372 } 373 374 for (size_t i = 0; i < 4; ++i) { 375 EXPECT_EQ(gDestructorCount[i], 0); 376 } 377} 378 379// Make sure that a wrapped vector does call the destructor when it's expected 380// as part of an API call 381TEST(DynamicVector, WrapExplicitlyCallsDestructor) { 382 resetDestructorCounts(); 383 384 Dummy array[4]; 385 constexpr size_t kSize = ARRAY_SIZE(array); 386 static_assert(ARRAY_SIZE(array) <= ARRAY_SIZE(gDestructorCount), 387 "gDestructorCount array must fit test array"); 388 for (size_t i = 0; i < kSize; ++i) { 389 array[i].setValue(i); 390 } 391 DynamicVector<Dummy> vector; 392 vector.wrap(array, ARRAY_SIZE(array)); 393 394 vector.erase(kSize - 1); 395 for (size_t i = 0; i < kSize - 1; i++) { 396 EXPECT_EQ(gDestructorCount[i], 0); 397 } 398 EXPECT_EQ(gDestructorCount[kSize - 1], 1); 399 400 vector.clear(); 401 for (size_t i = 0; i < kSize; ++i) { 402 EXPECT_EQ(gDestructorCount[i], 1); 403 } 404} 405 406TEST(DynamicVectorDeathTest, SwapWithInvalidIndex) { 407 DynamicVector<int> vector; 408 vector.push_back(0x1337); 409 vector.push_back(0xcafe); 410 EXPECT_DEATH(vector.swap(0, 2), ""); 411} 412 413TEST(DynamicVectorDeathTest, SwapWithInvalidIndices) { 414 DynamicVector<int> vector; 415 vector.push_back(0x1337); 416 vector.push_back(0xcafe); 417 EXPECT_DEATH(vector.swap(2, 3), ""); 418} 419 420TEST(DynamicVector, Swap) { 421 DynamicVector<int> vector; 422 vector.push_back(0x1337); 423 vector.push_back(0xcafe); 424 425 vector.swap(0, 1); 426 EXPECT_EQ(vector[0], 0xcafe); 427 EXPECT_EQ(vector[1], 0x1337); 428} 429 430TEST(DynamicVector, BackFront) { 431 DynamicVector<int> vector; 432 vector.push_back(0x1337); 433 EXPECT_EQ(vector.front(), 0x1337); 434 EXPECT_EQ(vector.back(), 0x1337); 435 vector.push_back(0xcafe); 436 EXPECT_EQ(vector.front(), 0x1337); 437 EXPECT_EQ(vector.back(), 0xcafe); 438 vector.erase(0); 439 EXPECT_EQ(vector.front(), 0xcafe); 440 EXPECT_EQ(vector.back(), 0xcafe); 441} 442 443TEST(DynamicVector, Iterator) { 444 DynamicVector<int> vector; 445 vector.push_back(0); 446 vector.push_back(1); 447 vector.push_back(2); 448 449 size_t index = 0; 450 for (DynamicVector<int>::iterator it = vector.begin(); 451 it != vector.end(); ++it) { 452 EXPECT_EQ(vector[index++], *it); 453 } 454 455 DynamicVector<int>::iterator it = vector.begin() + vector.size() - 1; 456 EXPECT_EQ(vector[vector.size() - 1], *it); 457 458 it = vector.begin() + vector.size(); 459 EXPECT_TRUE(it == vector.end()); 460} 461 462TEST(DynamicVector, ConstIterator) { 463 DynamicVector<int> vector; 464 vector.push_back(0); 465 vector.push_back(1); 466 vector.push_back(2); 467 468 size_t index = 0; 469 for (DynamicVector<int>::const_iterator cit = vector.cbegin(); 470 cit != vector.cend(); ++cit) { 471 EXPECT_EQ(vector[index++], *cit); 472 } 473 474 DynamicVector<int>::const_iterator cit = vector.cbegin() + vector.size() - 1; 475 EXPECT_EQ(vector[vector.size() - 1], *cit); 476 477 cit = vector.cbegin() + vector.size(); 478 EXPECT_TRUE(cit == vector.cend()); 479} 480 481TEST(DynamicVector, IteratorAndPushBack) { 482 DynamicVector<int> vector; 483 vector.push_back(0); 484 vector.push_back(1); 485 vector.push_back(2); 486 size_t oldCapacity = vector.capacity(); 487 488 DynamicVector<int>::iterator it_b = vector.begin(); 489 DynamicVector<int>::iterator it_e = vector.end(); 490 491 vector.push_back(3); 492 ASSERT_TRUE(oldCapacity == vector.capacity()); 493 494 size_t index = 0; 495 for (; it_b != it_e; ++it_b) { 496 EXPECT_EQ(vector[index++], *it_b); 497 } 498} 499 500TEST(DynamicVector, IteratorAndEmplaceBack) { 501 DynamicVector<int> vector; 502 vector.push_back(0); 503 vector.push_back(1); 504 vector.push_back(2); 505 size_t oldCapacity = vector.capacity(); 506 507 DynamicVector<int>::iterator it_b = vector.begin(); 508 DynamicVector<int>::iterator it_e = vector.end(); 509 510 vector.emplace_back(3); 511 ASSERT_TRUE(oldCapacity == vector.capacity()); 512 513 size_t index = 0; 514 for (; it_b != it_e; ++it_b) { 515 EXPECT_EQ(vector[index++], *it_b); 516 } 517} 518 519TEST(DynamicVector, IteratorAndReserve) { 520 DynamicVector<int> vector; 521 vector.push_back(0); 522 vector.push_back(1); 523 vector.push_back(2); 524 size_t oldCapacity = vector.capacity(); 525 526 DynamicVector<int>::iterator it_b = vector.begin(); 527 DynamicVector<int>::iterator it_e = vector.end(); 528 529 vector.reserve(oldCapacity); 530 ASSERT_TRUE(oldCapacity == vector.capacity()); 531 532 size_t index = 0; 533 for (; it_b != it_e; ++it_b) { 534 EXPECT_EQ(vector[index++], *it_b); 535 } 536} 537 538TEST(DynamicVector, IteratorAndInsert) { 539 DynamicVector<int> vector; 540 vector.push_back(0); 541 vector.push_back(1); 542 vector.push_back(2); 543 size_t oldCapacity = vector.capacity(); 544 545 DynamicVector<int>::iterator it_b = vector.begin(); 546 547 vector.insert(2, 3); 548 ASSERT_TRUE(oldCapacity == vector.capacity()); 549 550 size_t index = 0; 551 while (index < 2) { 552 EXPECT_EQ(vector[index++], *it_b++); 553 } 554} 555 556TEST(DynamicVector, IteratorAndErase) { 557 DynamicVector<int> vector; 558 vector.push_back(0); 559 vector.push_back(1); 560 vector.push_back(2); 561 562 DynamicVector<int>::iterator it_b = vector.begin(); 563 564 vector.erase(2); 565 566 size_t index = 0; 567 while (index < 2) { 568 EXPECT_EQ(vector[index++], *it_b++); 569 } 570} 571 572TEST(DynamicVector, IteratorAndSwap) { 573 DynamicVector<int> vector; 574 vector.push_back(0); 575 vector.push_back(1); 576 vector.push_back(2); 577 vector.push_back(3); 578 579 DynamicVector<int>::iterator it_b = vector.begin(); 580 581 vector.swap(1, 3); 582 583 size_t index = 0; 584 while (index < 4) { 585 if (index != 1 && index != 3) { 586 EXPECT_EQ(vector[index], *it_b); 587 } 588 index++; 589 it_b++; 590 } 591} 592 593TEST(DynamicVector, MoveConstruct) { 594 DynamicVector<int> vector; 595 ASSERT_TRUE(vector.push_back(0)); 596 ASSERT_TRUE(vector.push_back(1)); 597 ASSERT_TRUE(vector.push_back(2)); 598 599 DynamicVector<int> movedVector(std::move(vector)); 600 EXPECT_EQ(vector.data(), nullptr); 601 EXPECT_NE(movedVector.data(), nullptr); 602 EXPECT_EQ(vector.size(), 0); 603 EXPECT_EQ(movedVector.size(), 3); 604 EXPECT_EQ(vector.capacity(), 0); 605 EXPECT_EQ(movedVector.capacity(), 4); 606} 607 608// Tests basic functionality of a vector wrapping an array 609TEST(DynamicVector, Wrap) { 610 constexpr size_t kSize = 4; 611 int buf[kSize]; 612 for (size_t i = 0; i < kSize; i++) { 613 buf[i] = i; 614 } 615 616 DynamicVector<int> vector; 617 EXPECT_TRUE(vector.owns_data()); 618 vector.wrap(buf, kSize); 619 EXPECT_FALSE(vector.owns_data()); 620 EXPECT_EQ(vector.size(), kSize); 621 EXPECT_EQ(vector.capacity(), kSize); 622 EXPECT_EQ(vector.data(), buf); 623 624 EXPECT_CHRE_ASSERT(EXPECT_FALSE(vector.reserve(8))); 625 EXPECT_CHRE_ASSERT(EXPECT_FALSE(vector.push_back(-1))); 626 EXPECT_CHRE_ASSERT(EXPECT_FALSE(vector.emplace_back(-1))); 627 EXPECT_CHRE_ASSERT(EXPECT_FALSE(vector.insert(1, -1))); 628 629 for (size_t i = 0; i < kSize; i++) { 630 EXPECT_EQ(vector[i], i); 631 } 632 633 vector.erase(0); 634 for (size_t i = 0; i < kSize - 1; i++) { 635 EXPECT_EQ(vector[i], i + 1); 636 } 637 638 EXPECT_TRUE(vector.push_back(kSize + 1)); 639 EXPECT_EQ(vector.back(), kSize + 1); 640} 641 642TEST(DynamicVector, MoveWrappedVector) { 643 constexpr size_t kSize = 4; 644 int buf[kSize]; 645 for (size_t i = 0; i < kSize; i++) { 646 buf[i] = i; 647 } 648 649 DynamicVector<int> vector1; 650 vector1.wrap(buf, kSize); 651 652 DynamicVector<int> vector2 = std::move(vector1); 653 EXPECT_TRUE(vector1.owns_data()); 654 EXPECT_EQ(vector1.size(), 0); 655 EXPECT_EQ(vector1.capacity(), 0); 656 EXPECT_EQ(vector1.data(), nullptr); 657 658 EXPECT_FALSE(vector2.owns_data()); 659 EXPECT_EQ(vector2.size(), kSize); 660 EXPECT_EQ(vector2.capacity(), kSize); 661 EXPECT_EQ(vector2.data(), buf); 662} 663 664TEST(DynamicVector, Unwrap) { 665 constexpr size_t kSize = 4; 666 int buf[kSize]; 667 for (size_t i = 0; i < kSize; i++) { 668 buf[i] = i; 669 } 670 671 DynamicVector<int> vec; 672 vec.wrap(buf, kSize); 673 ASSERT_FALSE(vec.owns_data()); 674 675 vec.unwrap(); 676 EXPECT_TRUE(vec.owns_data()); 677 EXPECT_EQ(vec.size(), 0); 678 EXPECT_EQ(vec.capacity(), 0); 679 EXPECT_EQ(vec.data(), nullptr); 680 681 EXPECT_TRUE(vec.push_back(1)); 682} 683