Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

BoundingBox.H

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------*
00002  | Library QgarLib, graphics analysis and recognition                  |
00003  | Copyright (C) 2004  Qgar Project, LORIA                             |
00004  |                                                                     |
00005  | This library is free software; you can redistribute it and/or       |
00006  | modify it under the terms of the GNU Lesser General Public          |
00007  | License version 2.1, as published by the Free Software Foundation.  |
00008  |                                                                     |
00009  | This library is distributed in the hope that it will be useful,     |
00010  | but WITHOUT ANY WARRANTY; without even the implied warranty of      |
00011  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                |
00012  | See the GNU Lesser General Public License for more details.         |
00013  |                                                                     |
00014  | The GNU Lesser General Public License is included in the file       |
00015  | LICENSE.LGPL, in the root directory of the Qgar packaging. See      |
00016  | http://www.gnu.org/licenses/lgpl.html for the terms of the license. |
00017  | To receive a paper copy, write to the Free Software Foundation,     |
00018  | Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.       |
00019  |                                                                     |
00020  | Contact Project Qgar for any information:                           |
00021  |   LORIA - équipe Qgar                                               |
00022  |   B.P. 239, 54506 Vandoeuvre-lès-Nancy Cedex, France                |
00023  |   email: qgar-contact@loria.fr                                      |
00024  |   http://www.qgar.org/                                              |
00025  *---------------------------------------------------------------------*/
00026 
00027 
00028 #ifndef __BOUNDINGBOX_H_INCLUDED__
00029 #define __BOUNDINGBOX_H_INCLUDED__
00030 
00031 
00032 /**
00033  * @file   BoundingBox.H
00034  * @brief  Header file of class qgar::BoundingBox.
00035  *
00036  * @author <a href="mailto:qgar-develop@loria.fr?subject=Qgar fwd Jan Rendek">Jan Rendek</a>
00037  * @date   February 23, 2004  16:08
00038  * @since  Qgar 2.1.1
00039  */
00040 
00041 
00042 // For RCS/CVS use: Do not delete
00043 /* $Id: BoundingBox.H,v 1.21 2005/10/14 17:05:45 masini Exp $ */
00044 
00045 
00046 // STL
00047 #include <iosfwd> // Avoid including classes when not necessary
00048 // QGAR
00049 #include <qgarlib/ISerializable.H>
00050 #include <qgarlib/primitives.H>
00051 #include <qgarlib/QgarErrorDomain.H>
00052 
00053 
00054 
00055 namespace qgar
00056 {
00057 
00058 
00059 /**
00060  * @class BoundingBox BoundingBox.H "qgarlib/BoundingBox.H"
00061  * @ingroup DS_MISC
00062  * @brief A bounding box.
00063  *
00064  * A bounding box is a rectangle with sides parallel
00065  * to the coordinate system axis. It is represented
00066  * by its top left and bottom right corners.
00067  *
00068 @verbatim
00069    0
00070     +---------------------------------------> X
00071     |
00072     |            <---- length ---->
00073     |   top left o----------------+  ^
00074     |            |                |  |
00075     |            |  Bounding Box  |height
00076     |            |                |  |
00077     |            +----------------o  v
00078     V                            bottom right
00079 
00080     Y
00081 @endverbatim
00082  * @warning
00083  * <ul>
00084  * <li>A bounding box is supposed to be used
00085  *   within an <b>integer</b> coordinate system only.</li>
00086  * <li><b>As the origin of the coordinates system in images
00087  *   is at the top left corner of the plane, the top left and bottom
00088  *   right corners of the box are topographically upside down for
00089  *   more convenience.</b></li>
00090  * <li>The borders of a bounding box are part of the bounding box.
00091  *   In particular, the corresponding pixels are taken into account
00092  *   to compute the geometrical characteristics of the box:
00093  *   width, height, area...</li>
00094  * </ul>
00095  *
00096  * @author <a href="mailto:qgar-develop@loria.fr?subject=Qgar fwd Jan Rendek">Jan Rendek</a>
00097  * @date   February 23, 2004  16:08
00098  * @since  Qgar 2.1.1
00099  */
00100 class BoundingBox
00101 
00102   : public ISerializable
00103 
00104 {
00105 // -------------------------------------------------------------------
00106 // P U B L I C    M E M B E R S
00107 // -------------------------------------------------------------------
00108 public:
00109 
00110 
00111   /** @name Constructors */
00112   //        ============
00113   //@{
00114 
00115   /**
00116    * @brief Default constructor.
00117    *
00118    * One-unit-length and one-unit-width bounding box located
00119    * at the origin of the coordinate system.
00120    */
00121   BoundingBox();
00122 
00123   /**
00124    * @brief Copy constructor.
00125    *
00126    * @param aBox  a bounding box
00127    */
00128   BoundingBox(const BoundingBox& aBox);
00129 
00130   /**
00131    * @brief Initialize from two points.
00132    *
00133    * The two given points, <b>aPt1</b> and <b>aPt2</b>, are supposed
00134    * to be two opposite corners (top left and bottom right, or top right
00135    * and bottom left) of the resulting bounding box. The corresponding
00136    * coordinates of top left and bottom right corners are automatically
00137    * computed.
00138    *
00139    * @param aPt1  a point
00140    * @param aPt2  a point
00141    */
00142   BoundingBox(const GenPoint<int>& aPt1, const GenPoint<int>& aPt2);
00143   
00144   /**
00145    * @brief Initialize from coordinates of two points.
00146    *
00147    * The two given points are supposed to be two opposite corners (top
00148    * left and bottom right, or top right and bottom left) of the
00149    * resulting bounding box. The corresponding coordinates of top left
00150    * and bottom right corners are automatically computed.
00151    *
00152    * @param aX1  X coordinate of first point
00153    * @param aY1  Y coordinate of first point
00154    * @param aX2  X coordinate of second point
00155    * @param aY2  Y coordinate of second point
00156    */
00157   BoundingBox(int aX1, int aY1, int aX2, int aY2);
00158   
00159   /**
00160    * @brief Initialize from top left corner, length and height.
00161    *
00162    * The length and the height must be greater than 0.
00163    *
00164    * @param aTopLeft top left corner of the resulting bounding box
00165    * @param aLength  length of the resulting bounding box
00166    * @param aHeight  height of the resulting bounding box
00167    *
00168    * @exception
00169    *   qgar::QgarErrorDomain if the length or the height is not valid.
00170    */
00171   BoundingBox(const GenPoint<int>& aTopLeft, int aLength, int aHeight)
00172     throw(QgarErrorDomain);
00173   
00174   /**
00175    * @brief Initialize from bottom right corner, length and height.
00176    *
00177    * The length and the height must be greater than 0.
00178    *  
00179    * @param aLength       length of the resulting bounding box
00180    * @param aHeight       height of the resulting bounding box
00181    * @param aBottomRight  bottom right corner of the resulting bounding box
00182    *
00183    * @exception
00184    *   qgar::QgarErrorDomain if the length or the height is not valid.
00185    */
00186   BoundingBox(int aLength, int aHeight, const GenPoint<int>& aBottomRight)
00187     throw(QgarErrorDomain);
00188 
00189   //@}
00190   
00191   
00192   /** @name Destructor */
00193   //        ==========
00194   //@{
00195 
00196   /**
00197    * @brief Non-virtual destructor.
00198    */
00199   ~BoundingBox();
00200 
00201   //@}
00202   
00203   
00204   /** @name Access to corners */
00205   //        =================
00206   //@{
00207 
00208   /**
00209    * @brief Get top left corner.
00210    */
00211   inline const GenPoint<int>& accessTopLeft() const;
00212 
00213   /**
00214    * @brief Get a copy of the top left corner.
00215    */
00216   GenPoint<int> topLeft() const;
00217   
00218   /**
00219    * @brief Get bottom right corner.
00220    */
00221   inline const GenPoint<int>& accessBottomRight() const;
00222 
00223   /**
00224    * @brief Get a copy of the bottom right corner.
00225    */
00226   inline GenPoint<int> bottomRight() const;
00227 
00228   //@}
00229   
00230   
00231   /** @name Access to coordinates */
00232   //        =====================
00233   //@{
00234 
00235   /**
00236    * @brief Get X coordinate of top left corner.
00237    */
00238   inline int xTopLeft() const;
00239 
00240   /**
00241    * @brief Get Y coordinate of top left corner.
00242    */
00243   inline int yTopLeft() const;
00244   
00245   /**
00246    * @brief Get X coordinate of bottom right corner.
00247    */
00248   inline int xBottomRight() const;
00249 
00250   /**
00251    * @brief Get Y coordinate of bottom right corner.
00252    */
00253   inline int yBottomRight() const;
00254 
00255   //@}
00256   
00257 
00258   /** @name Access to dimensions */
00259   //        ====================
00260   //@{
00261 
00262   /**
00263    * @brief Same as qgar::BoundingBox::width.
00264    */
00265   inline int length() const;
00266 
00267   /**
00268    * @brief Get width.
00269    *
00270    * @warning As the pixels of the borders belong to the box,
00271    * the width is
00272    * \f$ X_{bottom-right-corner} - X_{top-left-corner} + 1 \f$
00273    */
00274   inline int width() const;
00275 
00276   /**
00277    * @brief Get height.
00278    *
00279    * @warning As the pixels of the borders belong to the box,
00280    * the width is
00281    * \f$ Y_{bottom-right-corner} - Y_{top-left-corner} + 1 \f$
00282    */
00283   inline int height() const;
00284 
00285   /**
00286    * @brief Get area.
00287    *
00288    * @warning The pixels of the borders belong to the box,
00289    * and thus are counted in the area.
00290    */
00291   inline int area() const;
00292 
00293   //@}
00294 
00295 
00296   /** @name Geometrical predicates */
00297   //        ======================
00298   //@{
00299 
00300   /**
00301    * @brief Return <b>true</b> if the given point belongs to
00302    *   the current bounding box, including borders.
00303    *
00304    * @param aPt   a point with <b>integer</b> coordinates
00305    */
00306   inline bool contains(const GenPoint<int>& aPt) const;
00307 
00308   /**
00309    * @brief Return <b>true</b> if the point defined
00310    *   by the given coordinates belongs to the current
00311    *   bounding box, including borders.
00312    *
00313    * @param aX    an <b>integer</b> X coordinate
00314    * @param aY    an <b>integer</b> Y coordinate
00315    */
00316   bool contains(int aX, int aY) const;
00317 
00318   /**
00319    * @brief Return <b>true</b> if the current bounding box
00320    *   contains the given bounding box.
00321    *
00322    * @param aBox  a bounding box
00323    */
00324   inline bool contains(const BoundingBox& aBox) const;
00325 
00326   /**
00327    * @brief Return <b>true</b> if the current bounding box
00328    *   intersects the given bounding box.
00329    *
00330    * @param aBox  a bounding box
00331    */
00332   bool intersects(const BoundingBox& aBox) const;
00333 
00334   //@}
00335 
00336 
00337   /** @name Geometrical operations */
00338   //        ======================
00339   //@{
00340 
00341   /**
00342    * @brief Intersection
00343    *   between the current and given bounding boxes.
00344    *
00345    * If the two bounding boxes intersect, returns a pointer
00346    * to the bounding box representing the intersection.
00347    * <b>If the two bounding boxes do not intersect, returns
00348    * a null pointer.</b>
00349    *
00350    * @param aBox  a bounding box
00351    */
00352   BoundingBox* intersection(const BoundingBox& aBox) const;
00353 
00354   //@}
00355 
00356 
00357   /** @name Set corners */
00358   //        ===========
00359   //@{
00360 
00361   /**
00362    * @brief Set top left corner using a point.
00363    *
00364    * @param aPt  a point
00365    *
00366    * @warning If the new corner (point <b>aPt</b>) does not match
00367    * the current bottom right corner, the function raises an exception.
00368    *
00369    * @exception qgar::QgarErrorDomain (new corner does not fit box)
00370    */
00371   void setTopLeft(const GenPoint<int>& aPt) throw(QgarErrorDomain);
00372 
00373   /**
00374    * @brief Set top left corner using X and Y coordinates.
00375    *
00376    * @param aX  X coordinate
00377    * @param aY  Y coordinate
00378    *
00379    * @warning If the new corner does not match the current bottom
00380    * right corner, the function raises an exception.
00381    *
00382    * @exception qgar::QgarErrorDomain (new corner does not fit box)
00383    */
00384   void setTopLeft(int aX, int aY) throw(QgarErrorDomain);
00385 
00386   /**
00387    * @brief Set X coordinate of top left corner.
00388    *
00389    * @param aX  X coordinate
00390    *
00391    * @warning If the new corner does not match the current bottom
00392    * right corner, the function raises an exception.
00393    *
00394    * @exception qgar::QgarErrorDomain (new corner does not fit box)
00395    */
00396   void setXTopLeft(int aX) throw(QgarErrorDomain);
00397 
00398   /**
00399    * @brief Set Y coordinate of top left corner.
00400    *
00401    * @param aY  Y coordinate
00402    *
00403    * @warning If the new corner does not match the current top left
00404    * corner, the function raises an exception.
00405    *
00406    * @exception qgar::QgarErrorDomain (new corner does not fit box)
00407    */
00408   void setYTopLeft(int aY) throw(QgarErrorDomain);
00409 
00410   /**
00411    * @brief Set bottom right corner using a point.
00412    *
00413    * @param aPt  a point
00414    *
00415    * @warning If the new corner (point <b>aPt</b>) does not match
00416    * the current top left corner, the function raises an exception.
00417    *
00418    * @exception qgar::QgarErrorDomain (new corner does not fit box)
00419    */
00420   void setBottomRight(const GenPoint<int>& aPt) throw(QgarErrorDomain);
00421 
00422   /**
00423    * @brief Set bottom right corner using X and Y coordinates.
00424    *
00425    * @param aX  X coordinate
00426    * @param aY  Y coordinate
00427    *
00428    * @warning If the new corner does not match the current top left
00429    * corner, the function raises an exception.
00430    *
00431    * @exception qgar::QgarErrorDomain (new corner does not fit box)
00432    */
00433   void setBottomRight(int aX, int aY) throw(QgarErrorDomain);
00434 
00435   /**
00436    * @brief Set X coordinate of bottom right corner.
00437    *
00438    * @param aX  X coordinate
00439    *
00440    * @warning If the new corner does not match the top left corner,
00441    * the function raises an exception.
00442    *
00443    * @exception qgar::QgarErrorDomain (new corner does not fit box)
00444    */
00445   void setXBottomRight(int aX) throw(QgarErrorDomain);
00446 
00447   /**
00448    * @brief Set Y coordinate of bottom right corner.
00449    *
00450    * @param aY  Y coordinate
00451    *
00452    * @warning If the new corner does not match the current top left
00453    * corner, the function raises an exception.
00454    *
00455    * @exception qgar::QgarErrorDomain (new corner does not fit box)
00456    */
00457   void setYBottomRight(int aY) throw(QgarErrorDomain);
00458 
00459   /**
00460    * @brief Set both top left and bottom right corners from two points.
00461    *
00462    * The two given points, <b>aPt1</b> and <b>aPt2</b>, are supposed
00463    * to be two opposite corners (top left and bottom right, or top
00464    * right and bottom left) of the resulting bounding box.
00465    * The corresponding coordinates of top left and bottom right
00466    * corners are automatically computed.
00467    *
00468    * @param aPt1  a point
00469    * @param aPt2  a point
00470    */
00471   void setCorners(const GenPoint<int>& aPt1, const GenPoint<int>& aPt2);
00472 
00473   /**
00474    * @brief Set both top left and bottom right corners
00475    * from coordinates of two points.
00476    *
00477    * The two given points are supposed to be two opposite corners
00478    * (top left and bottom right, or top right and bottom left)
00479    * of the resulting bounding box. The corresponding coordinates
00480    * of top left and bottom right corners are automatically computed.
00481    *
00482    * @param aX1  X coordinate of first corner
00483    * @param aY1  Y coordinate of first corner
00484    * @param aX2  X coordinate of second corner
00485    * @param aY2  Y coordinate of second corner
00486    */
00487   void setCorners(int aX1, int aY1, int aX2, int aY2);
00488 
00489   //@}
00490 
00491 
00492   /** @name Operators */
00493   //        =========
00494   //@{
00495 
00496   /**
00497    * @brief Assign the given bounding box to the current bounding box.
00498    *
00499    * @param aBox  a bounding box
00500    */
00501   BoundingBox& operator=(const BoundingBox& aBox);
00502 
00503   /**
00504    * @brief Same as function qgar::BoundingBox::eq.
00505    *
00506    * @param aBox  a bounding box
00507    */
00508   inline bool operator==(const BoundingBox& aBox) const;
00509 
00510   /**
00511    * @brief Same as function qgar::BoundingBox::NotEq.
00512    *
00513    * @param aBox  a bounding box
00514    */
00515   inline bool operator!=(const BoundingBox& aBox) const;
00516 
00517   //@}
00518 
00519 
00520   /**@name Functional operators */
00521   //       ====================
00522   //@{
00523 
00524    /**
00525    * @brief Equality.
00526    *
00527    * Return <b>true</b> if the current bounding box
00528    * and the given bounding box have the same coordinates.
00529    *
00530    * @param aBox  a bounding box
00531    */
00532   bool eq(const BoundingBox& aBox) const;
00533 
00534   /**
00535    * @brief Inequality.
00536    *
00537    * Return <b>true</b> if the current bounding box
00538    * and the given bounding box have different coordinates.
00539    *
00540    * @param aBox  a bounding box
00541    */
00542   bool notEq(const BoundingBox& aBox) const;
00543 
00544   //@}
00545 
00546 
00547   /** @name Serialization/deserialization */
00548   //        =============================
00549   //@{
00550 
00551   /**
00552    * @brief Deserializes the current box from an input stream.
00553    *
00554    * A serialized bounding box is represented as:
00555 @verbatim
00556    BoundingBox(<TOP LEFT CORNER>)(<BOTTOM RIGHT CORNER>)
00557 @endverbatim
00558    *
00559    * @param anInStream  the input stream
00560    */
00561   virtual std::istream& read(std::istream& anInStream);
00562 
00563   /**
00564    * @brief Serializes the current box to an input stream.
00565    *
00566    * A serialized bounding boxi is represented as:
00567 @verbatim
00568    BoundingBox(<TOP LEFT CORNER>)(<BOTTOM RIGHT CORNER>)
00569 @endverbatim
00570    *
00571    * @param anOutStream  the output stream
00572    */
00573   virtual std::ostream& write(std::ostream& anOutStream) const;
00574 
00575   //@}
00576 
00577 
00578 // -------------------------------------------------------------------
00579 // P R O T E C T E D    M E M B E R S
00580 // -------------------------------------------------------------------
00581 protected:
00582 
00583 
00584   /** @name Representation of a bounding box */
00585   //        ================================
00586   //@{
00587 
00588   /**
00589    * @brief Top left corner.
00590    */
00591   GenPoint<int> _topLeft;
00592 
00593   /**
00594    * @brief Bottom right corner.
00595    */
00596   GenPoint<int> _bottomRight;
00597 
00598   //@}
00599 
00600 // -------------------------------------------------------------------
00601 
00602 }; // class BoundingBox
00603 
00604 
00605 
00606 
00607 // IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
00608 // IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
00609 // I N L I N E   F U N C T I O N S   I M P L E M E N T A T I O N
00610 // IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
00611 // IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
00612 
00613 
00614 // =================
00615 // ACCESS TO CORNERS
00616 // =================
00617 
00618 
00619 // GET TOP LEFT CORNER
00620 
00621 inline const GenPoint<int>& 
00622 BoundingBox::accessTopLeft() const
00623 {
00624   return _topLeft;
00625 }
00626 
00627 
00628 // GET A COPY OF THE TOP LEFT CORNER
00629 
00630 inline GenPoint<int> 
00631 BoundingBox::topLeft() const
00632 {
00633   return _topLeft;
00634 }
00635 
00636 
00637 // GET BOTTOM RIGHT CORNER
00638 
00639 inline const GenPoint<int>& 
00640 BoundingBox::accessBottomRight() const
00641 {
00642   return _bottomRight;
00643 }
00644 
00645 
00646 // GET A COPY OF THE BOTTOM RIGHT CORNER
00647 
00648 inline GenPoint<int> 
00649 BoundingBox::bottomRight() const
00650 {
00651   return _bottomRight;
00652 }
00653 
00654 
00655 // =====================
00656 // ACCESS TO COORDINATES
00657 // =====================
00658 
00659 
00660 // GET X COORDINATE OF TOP LEFT CORNER
00661 
00662 inline int 
00663 BoundingBox::xTopLeft() const
00664 {
00665   return _topLeft.x();
00666 }
00667 
00668 
00669 // GET Y COORDINATE OF TOP LEFT CORNER
00670 
00671 inline int
00672 BoundingBox::yTopLeft() const
00673 {
00674   return _topLeft.y();
00675 }
00676 
00677 
00678 // GET X COORDINATE OF BOTTOM RIGHT CORNER
00679 
00680 inline int
00681 BoundingBox::xBottomRight() const
00682 {
00683   return _bottomRight.x();
00684 }
00685 
00686 
00687 // GET Y COORDINATE OF BOTTOM RIGHT CORNER
00688 
00689 inline int
00690 BoundingBox::yBottomRight() const
00691 {
00692   return _bottomRight.y();
00693 }
00694 
00695 
00696 // ====================
00697 // ACCESS TO DIMENSIONS
00698 // ====================
00699 
00700 
00701 // SAME AS qgar::BoundingBox::width
00702 
00703 inline int
00704 BoundingBox::length() const
00705 {
00706   return width();
00707 }
00708 
00709 
00710 // GET WIDTH
00711 
00712 inline int
00713 BoundingBox::width() const
00714 {
00715   return _bottomRight.x() - _topLeft.x() + 1;
00716 }
00717 
00718 
00719 // GET HEIGHT
00720 
00721 inline int
00722 BoundingBox::height() const
00723 {
00724   return _bottomRight.y() - _topLeft.y() + 1;
00725 }
00726 
00727 
00728 // GET AREA
00729 
00730 inline int
00731 BoundingBox::area() const
00732 {
00733   return length() * height();
00734 }
00735 
00736 
00737 // ===========================================
00738 // G E O M E T R I C A L   P R E D I C A T E S
00739 // ===========================================
00740 
00741 
00742 // DOES THE GIVEN POINT BELONG TO THE CURRENT BOUNDING BOX?
00743 
00744 inline bool
00745 BoundingBox::contains(const GenPoint<int>& aPt) const
00746 {
00747   return this->contains(aPt.x(), aPt.y());
00748 }
00749 
00750 
00751 // DOES THE CURRENT BOUNDING BOX CONTAIN THE GIVEN BOUNDING BOX?
00752 
00753 inline bool
00754 BoundingBox::contains(const BoundingBox& aBox) const
00755 {
00756   return (this->contains(aBox.topLeft()))
00757     && (this->contains(aBox.bottomRight()));
00758 }
00759 
00760 
00761 // =========
00762 // OPERATORS
00763 // =========
00764 
00765 
00766 // EQUALITY
00767 
00768 inline bool
00769 BoundingBox::operator==(const BoundingBox& aBox) const
00770 {
00771   return eq(aBox);
00772 }
00773 
00774 
00775 // INEQUALITY
00776 
00777 inline bool
00778 BoundingBox::operator!=(const BoundingBox& aBox) const
00779 {
00780   return notEq(aBox);
00781 }
00782 
00783 
00784 // IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
00785 // IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
00786 
00787 
00788 } // namespace qgar
00789 
00790 
00791 #endif /* __BOUNDINGBOX_H_INCLUDED__ */