1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.collection.ArrayList; 13 14 import std.algorithm; 15 import std.array; 16 import std.conv; 17 import std.traits; 18 19 import hunt.Exceptions; 20 import hunt.collection.Array; 21 import hunt.collection.AbstractList; 22 import hunt.collection.Collection; 23 import hunt.collection.List; 24 import hunt.util.Comparator; 25 26 /** 27 */ 28 class ArrayList(E) : AbstractList!E { 29 30 // private enum long serialVersionUID = 8683452581122892189L; 31 32 /** 33 * Default initial capacity. 34 */ 35 private enum int DEFAULT_CAPACITY = 10; 36 37 /** 38 * Shared empty array instance used for empty instances. 39 */ 40 private enum Object[] EMPTY_ELEMENTDATA = []; 41 42 /** 43 * Shared empty array instance used for default sized empty instances. We 44 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when 45 * first element is added. 46 */ 47 private enum Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = []; 48 49 50 protected Array!(E) _array; 51 52 /** 53 * Constructs an empty list with the specified initial capacity. 54 * 55 * @param initialCapacity the initial capacity of the list 56 * @throws Exception if the specified initial capacity 57 * is negative 58 */ 59 this(int initialCapacity) { 60 _array.reserve(initialCapacity); 61 super(); 62 // if (initialCapacity > 0) { 63 // this._array = new Object[initialCapacity]; 64 // } else if (initialCapacity == 0) { 65 // this._array = EMPTY_ELEMENTDATA; 66 // } else { 67 // throw new Exception("Illegal Capacity: "+ 68 // initialCapacity); 69 // } 70 } 71 72 /** 73 * Constructs an empty list with an initial capacity of ten. 74 */ 75 this() { 76 // this._array = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 77 _array.reserve(16); 78 super(); 79 } 80 81 /** 82 * Constructs a list containing the elements of the specified 83 * collection, in the order they are returned by the collection's 84 * iterator. 85 * 86 * @param c the collection whose elements are to be placed into this list 87 * @throws NullPointerException if the specified collection is null 88 */ 89 this(ref Array!E arr) { 90 _array = arr; 91 } 92 93 this(E[] arr) { 94 _array.insertBack(arr); 95 } 96 97 this(Collection!E c) { 98 foreach(E e; c) { 99 _array.insertBack(e); 100 } 101 } 102 103 /** 104 * Trims the capacity of this <tt>ArrayList</tt> instance to be the 105 * list's current size. An application can use this operation to minimize 106 * the storage of an <tt>ArrayList</tt> instance. 107 */ 108 // void trimToSize() { 109 // modCount++; 110 // if (size < _array.length) { 111 // _array = (size == 0) 112 // ? EMPTY_ELEMENTDATA 113 // : Arrays.copyOf(_array, size); 114 // } 115 // } 116 117 /** 118 * Increases the capacity of this <tt>ArrayList</tt> instance, if 119 * necessary, to ensure that it can hold at least the number of elements 120 * specified by the minimum capacity argument. 121 * 122 * @param minCapacity the desired minimum capacity 123 */ 124 // void ensureCapacity(int minCapacity) { 125 // int minExpand = (_array != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 126 // // any size if not default element table 127 // ? 0 128 // // larger than default for default empty table. It's already 129 // // supposed to be at default size. 130 // : DEFAULT_CAPACITY; 131 132 // if (minCapacity > minExpand) { 133 // ensureExplicitCapacity(minCapacity); 134 // } 135 // } 136 137 // private void ensureCapacityInternal(int minCapacity) { 138 // if (_array == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 139 // minCapacity = std.algorithm.max(DEFAULT_CAPACITY, minCapacity); 140 // } 141 142 // ensureExplicitCapacity(minCapacity); 143 // } 144 145 // private void ensureExplicitCapacity(int minCapacity) { 146 // modCount++; 147 148 // // overflow-conscious code 149 // if (minCapacity - _array.length > 0) 150 // grow(minCapacity); 151 // } 152 153 /** 154 * The maximum size of array to allocate. 155 * Some VMs reserve some header words in an array. 156 * Attempts to allocate larger arrays may result in 157 * OutOfMemoryError: Requested array size exceeds VM limit 158 */ 159 private enum int MAX_ARRAY_SIZE = int.max - 8; 160 161 162 163 /** 164 * Returns the number of elements in this list. 165 * 166 * @return the number of elements in this list 167 */ 168 override int size() { 169 return cast(int)_array.length; 170 } 171 172 /** 173 * Returns <tt>true</tt> if this list contains no elements. 174 * 175 * @return <tt>true</tt> if this list contains no elements 176 */ 177 override bool isEmpty() { 178 return _array.empty; 179 } 180 181 /** 182 * Returns <tt>true</tt> if this list contains the specified element. 183 * More formally, returns <tt>true</tt> if and only if this list contains 184 * at least one element <tt>e</tt> such that 185 * <tt>(o==null ? e==null : o.equals(e))</tt>. 186 * 187 * @param o element whose presence in this list is to be tested 188 * @return <tt>true</tt> if this list contains the specified element 189 */ 190 override bool contains(E o) const{ 191 return _array[].canFind(o); 192 } 193 194 195 /** 196 * Returns a shallow copy of this <tt>ArrayList</tt> instance. (The 197 * elements themselves are not copied.) 198 * 199 * @return a clone of this <tt>ArrayList</tt> instance 200 */ 201 // Object clone() { 202 203 // } 204 205 /** 206 * Returns an array containing all of the elements in this list 207 * in proper sequence (from first to last element). 208 * 209 * <p>The returned array will be "safe" in that no references to it are 210 * maintained by this list. (In other words, this method must allocate 211 * a new array). The caller is thus free to modify the returned array. 212 * 213 * <p>This method acts as bridge between array-based and collection-based 214 * APIs. 215 * 216 * @return an array containing all of the elements in this list in 217 * proper sequence 218 */ 219 override E[] toArray() { 220 return _array.array; 221 } 222 223 224 225 // Positional Access Operations 226 227 // 228 E elementData(int index) { 229 return _array[index]; 230 } 231 232 /** 233 * Returns the element at the specified position in this list. 234 * 235 * @param index index of the element to return 236 * @return the element at the specified position in this list 237 * @throws IndexOutOfBoundsException {@inheritDoc} 238 */ 239 override E get(int index) { 240 rangeCheck(index); 241 242 return cast(E)_array[index]; 243 } 244 245 /** 246 * Replaces the element at the specified position in this list with 247 * the specified element. 248 * 249 * @param index index of the element to replace 250 * @param element element to be stored at the specified position 251 * @return the element previously at the specified position 252 * @throws IndexOutOfBoundsException {@inheritDoc} 253 */ 254 override E set(int index, E element) { 255 rangeCheck(index); 256 257 E oldValue = _array[index]; 258 _array[index] = element; 259 return oldValue; 260 } 261 262 /** 263 * Appends the specified element to the end of this list. 264 * 265 * @param e element to be appended to this list 266 * @return <tt>true</tt> (as specified by {@link Collection#add}) 267 */ 268 // bool add(E e) { 269 // ensureCapacityInternal(size + 1); // Increments modCount!! 270 // _array[size++] = e; 271 // return true; 272 // } 273 override bool add(E e) { 274 return _array.insertBack(e) >=0; 275 } 276 277 /** 278 * Inserts the specified element at the specified position in this 279 * list. Shifts the element currently at that position (if any) and 280 * any subsequent elements to the right (adds one to their indices). 281 * 282 * @param index index at which the specified element is to be inserted 283 * @param element element to be inserted 284 * @throws IndexOutOfBoundsException {@inheritDoc} 285 */ 286 override void add(int index, E element) { 287 if(_array.length == 0) 288 _array.insertBack(element) ; 289 else { 290 if(index <= 0) 291 index = 1; 292 _array.insertAfter(_array[index-1 .. index], element); 293 } 294 } 295 296 alias add = AbstractList!(E).add; 297 298 /** 299 * Removes the element at the specified position in this list. 300 * Shifts any subsequent elements to the left (subtracts one from their 301 * indices). 302 * 303 * @param index the index of the element to be removed 304 * @return the element that was removed from the list 305 * @throws IndexOutOfBoundsException {@inheritDoc} 306 */ 307 override E removeAt(int index) { 308 E oldValue = _array[index]; 309 _array.linearRemove(_array[index .. index+1]); 310 return oldValue; 311 } 312 313 /** 314 * Removes the first occurrence of the specified element from this list, 315 * if it is present. If the list does not contain the element, it is 316 * unchanged. More formally, removes the element with the lowest index 317 * <tt>i</tt> such that 318 * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt> 319 * (if such an element exists). Returns <tt>true</tt> if this list 320 * contained the specified element (or equivalently, if this list 321 * changed as a result of the call). 322 * 323 * @param o element to be removed from this list, if present 324 * @return <tt>true</tt> if this list contained the specified element 325 */ 326 override bool remove(E o) 327 { 328 int index = indexOf(o); 329 if(index < 0) return false; 330 _array.linearRemove(_array[index .. index+1]); 331 return true; 332 } 333 334 override int indexOf(E o) { 335 for(size_t i=0; i<_array.length; i++) 336 { 337 static if(is(E == class)) 338 { 339 if(_array[i] is o) return cast(int)i; 340 } 341 else 342 { 343 if(_array[i] == o) return cast(int)i; 344 } 345 } 346 347 return -1; 348 } 349 350 override int lastIndexOf(E o) { 351 for(size_t i=_array.length -1; i>=0; i--) { 352 if(_array[i] == o) return cast(int)i; 353 } 354 355 return -1; 356 } 357 358 359 override int opApply(scope int delegate(ref E) dg) { 360 if(dg is null) 361 throw new NullPointerException(); 362 363 int result = 0; 364 foreach(E v; _array) { 365 result = dg(v); 366 if(result != 0) return result; 367 } 368 return result; 369 } 370 371 372 /* 373 * Private remove method that skips bounds checking and does not 374 * return the value removed. 375 */ 376 // private void fastRemove(int index) { 377 // modCount++; 378 // int numMoved = size - index - 1; 379 // if (numMoved > 0) 380 // System.arraycopy(_array, index+1, _array, index, 381 // numMoved); 382 // _array[--size] = null; // clear to let GC do its work 383 // } 384 385 /** 386 * Removes all of the elements from this list. The list will 387 * be empty after this call returns. 388 */ 389 override void clear() { 390 _array.clear(); 391 } 392 393 /** 394 * Appends all of the elements in the specified collection to the end of 395 * this list, in the order that they are returned by the 396 * specified collection's Iterator. The behavior of this operation is 397 * undefined if the specified collection is modified while the operation 398 * is in progress. (This implies that the behavior of this call is 399 * undefined if the specified collection is this list, and this 400 * list is nonempty.) 401 * 402 * @param c collection containing elements to be added to this list 403 * @return <tt>true</tt> if this list changed as a result of the call 404 * @throws NullPointerException if the specified collection is null 405 */ 406 // bool addAll(Collection<E> c) { 407 // Object[] a = c.toArray(); 408 // int numNew = a.length; 409 // ensureCapacityInternal(size + numNew); // Increments modCount 410 // System.arraycopy(a, 0, _array, size, numNew); 411 // size += numNew; 412 // return numNew != 0; 413 // } 414 415 /** 416 * Inserts all of the elements in the specified collection into this 417 * list, starting at the specified position. Shifts the element 418 * currently at that position (if any) and any subsequent elements to 419 * the right (increases their indices). The new elements will appear 420 * in the list in the order that they are returned by the 421 * specified collection's iterator. 422 * 423 * @param index index at which to insert the first element from the 424 * specified collection 425 * @param c collection containing elements to be added to this list 426 * @return <tt>true</tt> if this list changed as a result of the call 427 * @throws IndexOutOfBoundsException {@inheritDoc} 428 * @throws NullPointerException if the specified collection is null 429 */ 430 // bool addAll(int index, Collection<E> c) { 431 // rangeCheckForAdd(index); 432 433 // Object[] a = c.toArray(); 434 // int numNew = a.length; 435 // ensureCapacityInternal(size + numNew); // Increments modCount 436 437 // int numMoved = size - index; 438 // if (numMoved > 0) 439 // System.arraycopy(_array, index, _array, index + numNew, 440 // numMoved); 441 442 // System.arraycopy(a, 0, _array, index, numNew); 443 // size += numNew; 444 // return numNew != 0; 445 // } 446 447 /** 448 * Removes from this list all of the elements whose index is between 449 * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. 450 * Shifts any succeeding elements to the left (reduces their index). 451 * This call shortens the list by {@code (toIndex - fromIndex)} elements. 452 * (If {@code toIndex==fromIndex}, this operation has no effect.) 453 * 454 * @throws IndexOutOfBoundsException if {@code fromIndex} or 455 * {@code toIndex} is out of range 456 * ({@code fromIndex < 0 || 457 * fromIndex >= size() || 458 * toIndex > size() || 459 * toIndex < fromIndex}) 460 */ 461 protected void removeRange(int fromIndex, int toIndex) { 462 _array.linearRemove(_array[fromIndex..toIndex]); 463 } 464 465 /** 466 * Checks if the given index is in range. If not, throws an appropriate 467 * runtime exception. This method does *not* check if the index is 468 * negative: It is always used immediately prior to an array access, 469 * which throws an ArrayIndexOutOfBoundsException if index is negative. 470 */ 471 private void rangeCheck(int index) { 472 if (index >= _array.length) 473 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 474 } 475 476 /** 477 * A version of rangeCheck used by add and addAll. 478 */ 479 private void rangeCheckForAdd(int index) { 480 if (index > _array.length || index < 0) 481 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 482 } 483 484 /** 485 * Constructs an IndexOutOfBoundsException detail message. 486 * Of the many possible refactorings of the error handling code, 487 * this "outlining" performs best with both server and client VMs. 488 */ 489 private string outOfBoundsMsg(int index) { 490 return "Index: "~index.to!string()~", Size: " ~ to!string(size()); 491 } 492 493 /** 494 * Removes from this list all of its elements that are contained in the 495 * specified collection. 496 * 497 * @param c collection containing elements to be removed from this list 498 * @return {@code true} if this list changed as a result of the call 499 * @throws ClassCastException if the class of an element of this list 500 * is incompatible with the specified collection 501 * (<a href="Collection.html#optional-restrictions">optional</a>) 502 * @throws NullPointerException if this list contains a null element and the 503 * specified collection does not permit null elements 504 * (<a href="Collection.html#optional-restrictions">optional</a>), 505 * or if the specified collection is null 506 * @see Collection#contains(Object) 507 */ 508 // bool removeAll(Collection<?> c) { 509 // Objects.requireNonNull(c); 510 // return batchRemove(c, false); 511 // } 512 513 /** 514 * Retains only the elements in this list that are contained in the 515 * specified collection. In other words, removes from this list all 516 * of its elements that are not contained in the specified collection. 517 * 518 * @param c collection containing elements to be retained in this list 519 * @return {@code true} if this list changed as a result of the call 520 * @throws ClassCastException if the class of an element of this list 521 * is incompatible with the specified collection 522 * (<a href="Collection.html#optional-restrictions">optional</a>) 523 * @throws NullPointerException if this list contains a null element and the 524 * specified collection does not permit null elements 525 * (<a href="Collection.html#optional-restrictions">optional</a>), 526 * or if the specified collection is null 527 * @see Collection#contains(Object) 528 */ 529 // bool retainAll(Collection!E c) { 530 // assert(c !is null); 531 // // return batchRemove(c, true); 532 533 // _array[].remove!(x => !c.canFind(x)); 534 // return true; 535 // } 536 537 // private bool batchRemove(Collection<?> c, bool complement) { 538 // final Object[] _array = this._array; 539 // int r = 0, w = 0; 540 // bool modified = false; 541 // try { 542 // for (; r < size; r++) 543 // if (c.contains(_array[r]) == complement) 544 // _array[w++] = _array[r]; 545 // } finally { 546 // // Preserve behavioral compatibility with AbstractCollection, 547 // // even if c.contains() throws. 548 // if (r != size) { 549 // System.arraycopy(_array, r, 550 // _array, w, 551 // size - r); 552 // w += size - r; 553 // } 554 // if (w != size) { 555 // // clear to let GC do its work 556 // for (int i = w; i < size; i++) 557 // _array[i] = null; 558 // modCount += size - w; 559 // size = w; 560 // modified = true; 561 // } 562 // } 563 // return modified; 564 // } 565 566 /** 567 * Save the state of the <tt>ArrayList</tt> instance to a stream (that 568 * is, serialize it). 569 * 570 * @serialData The length of the array backing the <tt>ArrayList</tt> 571 * instance is emitted (int), followed by all of its elements 572 * (each an <tt>Object</tt>) in the proper order. 573 */ 574 // private void writeObject(java.io.ObjectOutputStream s) 575 // throws java.io.IOException{ 576 // // Write out element count, and any hidden stuff 577 // int expectedModCount = modCount; 578 // s.defaultWriteObject(); 579 580 // // Write out size as capacity for behavioural compatibility with clone() 581 // s.writeInt(size); 582 583 // // Write out all elements in the proper order. 584 // for (int i=0; i<size; i++) { 585 // s.writeObject(_array[i]); 586 // } 587 588 // if (modCount != expectedModCount) { 589 // throw new ConcurrentModificationException(); 590 // } 591 // } 592 593 /** 594 * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is, 595 * deserialize it). 596 */ 597 // private void readObject(java.io.ObjectInputStream s) 598 // throws java.io.IOException, ClassNotFoundException { 599 // _array = EMPTY_ELEMENTDATA; 600 601 // // Read in size, and any hidden stuff 602 // s.defaultReadObject(); 603 604 // // Read in capacity 605 // s.readInt(); // ignored 606 607 // if (size > 0) { 608 // // be like clone(), allocate array based upon size not capacity 609 // ensureCapacityInternal(size); 610 611 // Object[] a = _array; 612 // // Read in all elements in the proper order. 613 // for (int i=0; i<size; i++) { 614 // a[i] = s.readObject(); 615 // } 616 // } 617 // } 618 619 620 /** 621 * Returns a view of the portion of this list between the specified 622 * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. (If 623 * {@code fromIndex} and {@code toIndex} are equal, the returned list is 624 * empty.) The returned list is backed by this list, so non-structural 625 * changes in the returned list are reflected in this list, and vice-versa. 626 * The returned list supports all of the optional list operations. 627 * 628 * <p>This method eliminates the need for explicit range operations (of 629 * the sort that commonly exist for arrays). Any operation that expects 630 * a list can be used as a range operation by passing a subList view 631 * instead of a whole list. For example, the following idiom 632 * removes a range of elements from a list: 633 * <pre> 634 * list.subList(from, to).clear(); 635 * </pre> 636 * Similar idioms may be constructed for {@link #indexOf(Object)} and 637 * {@link #lastIndexOf(Object)}, and all of the algorithms in the 638 * {@link Collections} class can be applied to a subList. 639 * 640 * <p>The semantics of the list returned by this method become undefined if 641 * the backing list (i.e., this list) is <i>structurally modified</i> in 642 * any way other than via the returned list. (Structural modifications are 643 * those that change the size of this list, or otherwise perturb it in such 644 * a fashion that iterations in progress may yield incorrect results.) 645 * 646 * @throws IndexOutOfBoundsException {@inheritDoc} 647 * @throws Exception {@inheritDoc} 648 */ 649 // List<E> subList(int fromIndex, int toIndex) { 650 // subListRangeCheck(fromIndex, toIndex, size); 651 // return new SubList(this, 0, fromIndex, toIndex); 652 // } 653 654 // static void subListRangeCheck(int fromIndex, int toIndex, int size) { 655 // if (fromIndex < 0) 656 // throw new IndexOutOfBoundsException("fromIndex = " ~ fromIndex); 657 // if (toIndex > size) 658 // throw new IndexOutOfBoundsException("toIndex = " ~ toIndex); 659 // if (fromIndex > toIndex) 660 // throw new Exception("fromIndex(" ~ fromIndex + 661 // ") > toIndex(" ~ toIndex ~ ")"); 662 // } 663 664 static if (isOrderingComparable!E) { 665 override void sort(bool isAscending = true) { 666 667 // https://issues.dlang.org/show_bug.cgi?id=15304 668 // std.algorithm.sort(_array[]); 669 670 int expectedModCount = modCount; 671 if(isAscending) 672 std.algorithm.sort!(lessThan!E)(_array[]); 673 else 674 std.algorithm.sort!(greaterthan!E)(_array[]); 675 676 if (modCount != expectedModCount) 677 throw new ConcurrentModificationException(); 678 modCount++; 679 } 680 } 681 682 683 override void sort(Comparator!E c) { 684 int expectedModCount = modCount; 685 std.algorithm.sort!((a, b) => c.compare(a, b) < 0)(_array[]); 686 687 if (modCount != expectedModCount) 688 throw new ConcurrentModificationException(); 689 modCount++; 690 } 691 692 } 693 694 /** 695 * Lazy List creation. 696 * <p> 697 * A List helper class that attempts to avoid unnecessary List creation. If a 698 * method needs to create a List to return, but it is expected that this will 699 * either be empty or frequently contain a single item, then using LazyList will 700 * avoid additional object creations by using {@link Collections#EMPTY_LIST} or 701 * {@link Collections#singletonList(Object)} where possible. 702 * </p> 703 * <p> 704 * LazyList works by passing an opaque representation of the list in and out of 705 * all the LazyList methods. This opaque object is either null for an empty 706 * list, an Object for a list with a single entry or an {@link ArrayList} for a 707 * list of items. 708 * </p> 709 * <strong>Usage</strong> 710 * 711 * <pre> 712 * Object lazylist = null; 713 * while (loopCondition) { 714 * Object item = getItem(); 715 * if (item.isToBeAdded()) 716 * lazylist = LazyList.add(lazylist, item); 717 * } 718 * return LazyList.getList(lazylist); 719 * </pre> 720 * 721 * An ArrayList of default size is used as the initial LazyList. 722 * 723 * @see java.util.List 724 */ 725 class LazyList 726 { 727 // this(){ 728 729 // } 730 731 /** 732 * Add an item to a LazyList 733 * 734 * @param list 735 * The list to add to or null if none yet created. 736 * @param item 737 * The item to add. 738 * @return The lazylist created or added to. 739 */ 740 static Object add(Object list, Object item) { 741 if (list is null) { 742 if (typeid(item) == typeid(List!Object) || item is null) { 743 List!Object l = new ArrayList!Object(); 744 l.add(item); 745 return cast(Object)l; 746 } 747 748 return item; 749 } 750 751 if (typeid(list) == typeid(List!Object)) { 752 (cast(List!Object) list).add(item); 753 return list; 754 } 755 756 List!Object l = new ArrayList!Object(); 757 l.add(list); 758 l.add(item); 759 return cast(Object)l; 760 } 761 762 /** 763 * Get the real List from a LazyList. 764 * 765 * @param list 766 * A LazyList returned from LazyList.add(Object) or null 767 * @param nullForEmpty 768 * If true, null is returned instead of an empty list. 769 * @return The List of added items, which may be null, an EMPTY_LIST or a 770 * SingletonList. 771 * @param <E> 772 * the list entry type 773 */ 774 static List!E getList(E)(Object list, bool nullForEmpty) { 775 if (list is null) { 776 if (nullForEmpty) 777 return null; 778 return new EmptyList!E(); // Collections.emptyList(); 779 } 780 List!E r = cast(List!E) list; 781 if (r !is null) 782 return r; 783 784 // return (List<E>) Collections.singletonList(list); 785 auto l = new ArrayList!E(); 786 l.add(cast(E)list); 787 return l; 788 } 789 790 791 /** 792 * The size of a lazy List 793 * 794 * @param list 795 * A LazyList returned from LazyList.add(Object) or null 796 * @return the size of the list. 797 */ 798 static int size(T)(List!(T) list) { 799 if (list is null) 800 return 0; 801 return list.size(); 802 } 803 804 }