milena r1563: Add run-based utilities

URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena ChangeLog: 2007-11-28 Simon Nivault <simon.nivault@lrde.epita.fr> Add run-based utilities. * mln/core/internal/run_pset.hh: Point set based on many runs. * mln/core/p_array_piter.hh: Add FIXME. * mln/core/p_run.hh: New, Point set based on a run. * mln/core/p_run_piter.hh: New. * mln/util/lazy_set.hh: New, lazy vector of sorted elements. * tests/run_pset.cc: Update. --- mln/core/internal/run_pset.hh | 58 +++++-- mln/core/p_array_piter.hh | 2 mln/core/p_run.hh | 226 +++++++++++++++++++++++++++++ mln/core/p_run_piter.hh | 266 ++++++++++++++++++++++++++++++++++ mln/util/lazy_set.hh | 320 ++++++++++++++++++++++++++++++++++++++++++ tests/run_pset.cc | 7 6 files changed, 858 insertions(+), 21 deletions(-) Index: trunk/milena/tests/run_pset.cc =================================================================== --- trunk/milena/tests/run_pset.cc (revision 1562) +++ trunk/milena/tests/run_pset.cc (revision 1563) @@ -63,17 +63,18 @@ // Pset test internal::run_pset_<point2d> ps; - ps.insert(p, 7); + ps.insert(p_run<point2d>(p, 7)); mln_assertion(ps.npoints() == 7); - ps.insert(q, 42); + ps.insert(p_run<point2d>(q, 42)); mln_assertion(ps.npoints() == 49); mln_assertion(ps.has(site)); mln_assertion(!ps.has(site2)); - ps.insert(r, 14); + ps.insert(p_run<point2d>(r, 14)); mln_assertion(!ps.has(site2)); + ps.insert(p_run<point2d>(make::point2d(17,40), 6)); // parc(ps); } Index: trunk/milena/mln/core/internal/run_pset.hh =================================================================== --- trunk/milena/mln/core/internal/run_pset.hh (revision 1562) +++ trunk/milena/mln/core/internal/run_pset.hh (revision 1563) @@ -37,9 +37,10 @@ # include <mln/core/internal/point_set_base.hh> # include <mln/core/internal/point_iterator_base.hh> # include <mln/core/internal/run_psite.hh> +# include <mln/core/p_run.hh> # include <mln/accu/bbox.hh> +# include <mln/util/lazy_set.hh> -# include <vector> # include <utility> @@ -63,7 +64,7 @@ { public: - typedef std::vector<std::pair<P, unsigned> > std_container; + typedef util::lazy_set_<p_run<P> > container; typedef run_fwd_piter_<P> fwd_piter; typedef run_bkd_piter_<P> bkd_piter; @@ -80,13 +81,13 @@ typename std::size_t npoints() const; /// Insert a range, start at point \p p wit len \p len. - void insert(const P& p, unsigned len); + void insert(const p_run<P>& pr); /// Return the len of the range starting at point \p p. unsigned range_len_(const P& p) const; /// Return the container of the pset (internal use only). - const std_container& con() const; + const container& con() const; protected: @@ -94,7 +95,7 @@ typename std::size_t npoints_; /// Points container - std_container con_; + container con_; /// Exact bounding box. accu::bbox<P> fb_; @@ -112,9 +113,9 @@ bool run_pset_<P>::has(const run_psite<P>& p) const { - for (unsigned i = 0; i < con_.size(); ++i) + for (unsigned i = 0; i < con_.nelements(); ++i) { - if (con_[i].first == p.range_start_() && con_[i].second > p.index_()) + if (con_[i].first() == p.range_start_() && con_[i].length() > p.index_()) return true; } return false; @@ -136,19 +137,42 @@ template <typename P> void - run_pset_<P>::insert(const P& p, unsigned len) + run_pset_<P>::insert(const p_run<P>& pr) { - P run_pend; - typename std_container::value_type elt (p, len); - con_.push_back(elt); + typename std::vector<p_run<P> >::const_iterator iter = con_.vect().begin(); + while (iter != con_.vect().end() && iter->first() < pr.first()) + ++iter; + + if (iter != con_.vect().begin()) + { + typename std::vector<p_run<P> >::const_iterator prec = iter; + --prec; + bool equal = true; + for (int i = P::dim - 2; i >= 0; --i) + if (!(equal = equal && (prec->first()[i] == pr.first()[i]))) + break; + if (equal) + mln_assertion(prec->first()[P::dim - 1] + (signed)prec->length() + < pr.first()[P::dim - 1]); + } + + if (iter != con_.vect().end()) + { + bool equal = true; + for (int i = P::dim - 2; i >= 0; --i) + if (!(equal = equal && ((*iter).first()[i] == pr.first()[i]))) + break; + if (equal) + mln_assertion(pr.first()[P::dim - 1] + (signed)pr.length() + < iter->first()[P::dim - 1]); + } + con_.insert(pr); // update box - fb_.take(p); - run_pend = p; - run_pend[0] += len - 1; - fb_.take(run_pend); + fb_.take(pr.bbox().pmin()); + fb_.take(pr.bbox().pmax()); // update size - npoints_ += len; + npoints_ += pr.npoints(); } template <typename P> @@ -168,7 +192,7 @@ } template <typename P> - const typename run_pset_<P>::std_container& + const typename run_pset_<P>::container& run_pset_<P>::con() const { return con_; Index: trunk/milena/mln/core/p_run.hh =================================================================== --- trunk/milena/mln/core/p_run.hh (revision 0) +++ trunk/milena/mln/core/p_run.hh (revision 1563) @@ -0,0 +1,226 @@ +// Copyright (C) 2007 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 51 Franklin Street, Fifth Floor, +// Boston, MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef MLN_CORE_P_RUN_HH +# define MLN_CORE_P_RUN_HH + +/*! \file mln/core/p_run.hh + * + * \brief Definition of a point set class based on std::set. + */ + +# include <mln/core/internal/point_set_base.hh> +# include <mln/core/internal/set_of.hh> +# include <mln/accu/bbox.hh> + + +namespace mln +{ + + // Fwd decls. + template <typename P> struct p_run_fwd_piter_; + template <typename P> struct p_run_bkd_piter_; + + /*! \brief Point set class in run. + * + * This is a mathematical set of points (not a multi-set). The + * parameter \p P shall be a Point type. + * + * \todo Test if \p P being a Point_Site is ok. + */ + template <typename P> + class p_run : public internal::point_set_base_< P, p_run<P> > + { + public: + + /// Forward Point_Iterator associated type. + typedef p_run_fwd_piter_<P> fwd_piter; + + /// Backward Point_Iterator associated type. + typedef p_run_bkd_piter_<P> bkd_piter; + + /// Constructor without argument. + p_run(); + + /// Constructor. + p_run(const P& start, std::size_t len); + + /// Set the starting point. + void set_run(const P& start, std::size_t len); + + /// Test is \p p belongs to this point set. + bool has(const P& p) const; + + /// Give the number of points. + std::size_t npoints() const; + + /// Give the length of the run. + std::size_t length() const; + + /// Return the \p i-th point. + P operator[](unsigned i) const; + + /// Return the first point. + const P& first() const; + + /// Give the exact bounding box. + const box_<mln_point(P)>& bbox() const; + + /// Set a relation order to p_run. + bool operator<(const p_run<P>& rhs) const; + + protected: + + accu::bbox<P> bb_; + // FIXME: Add invariant bb_.is_valid() <=> npoints() != 0 + + /// The first point of the run. + P p_; + + /// The length of the run. + std::size_t len_; + + /// For internal use. + bool is_valid_; + }; + + template <typename P> + std::ostream& operator<<(std::ostream& out, const p_run<P>& pr) + { + out << "Run: (" << pr.first() << ", " << pr.length() << ")"; + return out; + } + +# ifndef MLN_INCLUDE_ONLY + + template <typename P> + p_run<P>::p_run() + { + is_valid_ = false; + } + + template <typename P> + p_run<P>::p_run(const P& start, std::size_t len) + : p_(start), + len_(len) + { + mln_precondition(len != 0); + P p = start; + bb_.init(); + bb_.take(p); + p[P::dim - 1] += len - 1; + bb_.take(p); + is_valid_ = true; + } + + template <typename P> + void + p_run<P>::set_run(const P& start, std::size_t len) + { + mln_precondition(len != 0); + p_ = start; + len_ = len; + P p = start; + bb_.init(); + bb_.take(p); + p[P::dim - 1] += len - 1; + bb_.take(p); + is_valid_ = true; + } + + template <typename P> + bool + p_run<P>::has(const P& p) const + { + mln_precondition(is_valid_); + bool res = true; + for (int i = P::dim - 2; i >= 0; --i) + if (!(res = (res && p[i] == p_[i]))) + return false; + return (p[P::dim - 1] >= p_[P::dim - 1] + && p[P::dim - 1] < p_[P::dim - 1] + (signed)len_); + } + + template <typename P> + std::size_t + p_run<P>::npoints() const + { + mln_precondition(is_valid_); + return len_; + } + + template <typename P> + std::size_t + p_run<P>::length() const + { + mln_precondition(is_valid_); + return len_; + } + + template <typename P> + P + p_run<P>::operator[](unsigned i) const + { + mln_precondition(is_valid_); + mln_precondition(i < npoints()); + P p = p_; + p[P::dim - 1] += i; + return p; + } + + template <typename P> + const P& + p_run<P>::first() const + { + return p_; + } + + template <typename P> + const box_<mln_point(P)>& + p_run<P>::bbox() const + { + mln_precondition(is_valid_); + mln_precondition(npoints() != 0); + return bb_.to_result(); + } + + template <typename P> + bool + p_run<P>::operator<(const p_run<P>& rhs) const + { + return (this->p_ < rhs.p_) + || (this->p_ == rhs.p_ && this->len_ < rhs.len_); + } + + +# endif // ! MLN_INCLUDE_ONLY + +} // end of namespace mln + +# include <mln/core/p_run_piter.hh> + +#endif // ! MLN_CORE_P_RUN_HH Index: trunk/milena/mln/core/p_run_piter.hh =================================================================== --- trunk/milena/mln/core/p_run_piter.hh (revision 0) +++ trunk/milena/mln/core/p_run_piter.hh (revision 1563) @@ -0,0 +1,266 @@ +// Copyright (C) 2007 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 51 Franklin Street, Fifth Floor, +// Boston, MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef MLN_CORE_P_RUN_PITER_HH +# define MLN_CORE_P_RUN_PITER_HH + +/*! \file mln/core/p_run_piter.hh + * + * \brief Definition of point iterators on mln::p_run. + */ + +# include <mln/core/p_run.hh> + + +namespace mln +{ + + /*! \brief Forward iterator on points of a p_run<P>. + * + */ + template <typename P> + struct p_run_fwd_piter_ : public internal::point_iterator_base_< P, p_run_fwd_piter_<P> > + { + typedef p_run_fwd_piter_<P> self_; + typedef internal::point_iterator_base_< P, self_ > super_; + public: + + // Make definitions from super class available. + enum { dim = super_::dim }; + + /// Coordinate associated type. + p_run_fwd_piter_(const p_run<P>& pr); + + /// Reference of the corresponding point. + const P& to_point() const; + + /// Read-only access to the \p i-th coordinate. + mln_coord(P) operator[](unsigned i) const; + + /// Test if the iterator is valid. + bool is_valid() const; + + /// Invalidate the iterator. + void invalidate(); + + /// Start an iteration. + void start(); + + /// Go to the next point. + void next_(); + + /// Convert the iterator into a point. + operator P() const; + + protected: + const p_run<P>& run_; + bool is_valid_; + P p_; + }; + + + + /*! \brief Backward iterator on points of a p_run<P>. + * + */ + template <typename P> + struct p_run_bkd_piter_ : public internal::point_iterator_base_< P, p_run_bkd_piter_<P> > + { + typedef p_run_bkd_piter_<P> self_; + typedef internal::point_iterator_base_< P, self_ > super_; + public: + + // Make definitions from super class available. + enum { dim = super_::dim }; + + /// Coordinate associated type. + p_run_bkd_piter_(const p_run<P>& pr); + + /// Reference of the corresponding point. + const P& to_point() const; + + /// Read-only access to the \p i-th coordinate. + mln_coord(P) operator[](unsigned i) const; + + /// Test if the iterator is valid. + bool is_valid() const; + + /// Invalidate the iterator. + void invalidate(); + + /// Start an iteration. + void start(); + + /// Go to the next point. + void next_(); + + /// Convert the iterator into a point. + operator P() const; + + protected: + const p_run<P>& run_; + bool is_valid_; + P p_; + }; + + + +# ifndef MLN_INCLUDE_ONLY + + // p_run_fwd_piter_<P> + + template <typename P> + p_run_fwd_piter_<P>::p_run_fwd_piter_(const p_run<P>& pr) + : run_(pr) + { + invalidate(); + } + + template <typename P> + const P& + p_run_fwd_piter_<P>::to_point() const + { + mln_precondition(is_valid()); + return p_; + } + + template <typename P> + mln_coord(P) + p_run_fwd_piter_<P>::operator[](unsigned i) const + { + mln_precondition(i < dim); + mln_precondition(is_valid()); + return p_[i]; + } + + template <typename P> + bool + p_run_fwd_piter_<P>::is_valid() const + { + return is_valid_; + } + + template <typename P> + void + p_run_fwd_piter_<P>::invalidate() + { + is_valid_ = false; + } + + template <typename P> + void + p_run_fwd_piter_<P>::start() + { + p_ = run_.first(); + is_valid_ = true; + } + + template <typename P> + void + p_run_fwd_piter_<P>::next_() + { + p_[dim - 1]++; + is_valid_ = p_[dim - 1] - run_.first()[dim - 1] < (signed)run_.length(); + } + + template <typename P> + p_run_fwd_piter_<P>::operator P() const + { + mln_precondition(is_valid()); + return p_; + } + + + // p_run_bkd_piter_<P> + + template <typename P> + p_run_bkd_piter_<P>::p_run_bkd_piter_(const p_run<P>& pr) + : run_(pr) + { + invalidate(); + } + + template <typename P> + const P& + p_run_bkd_piter_<P>::to_point() const + { + mln_precondition(is_valid()); + return p_; + } + + template <typename P> + mln_coord(P) + p_run_bkd_piter_<P>::operator[](unsigned i) const + { + mln_precondition(i < dim); + mln_precondition(is_valid()); + return p_[i]; + } + + template <typename P> + bool + p_run_bkd_piter_<P>::is_valid() const + { + return is_valid; + } + + template <typename P> + void + p_run_bkd_piter_<P>::invalidate() + { + is_valid_ = false; + } + + template <typename P> + void + p_run_bkd_piter_<P>::start() + { + p_ = run_[run_.length() - 1]; + is_valid_ = true; +} + + template <typename P> + void + p_run_bkd_piter_<P>::next_() + { + p_[dim - 1]++; + is_valid_ = p_[dim - 1] - run_.first()[dim - 1] >= 0; + } + + template <typename P> + p_run_bkd_piter_<P>::operator P() const + { + mln_precondition(is_valid()); + return p_; + } + +# endif // ! MLN_INCLUDE_ONLY + +} // end of namespace mln + + +#endif // ! MLN_CORE_P_RUN_PITER_HH Index: trunk/milena/mln/core/p_array_piter.hh =================================================================== --- trunk/milena/mln/core/p_array_piter.hh (revision 1562) +++ trunk/milena/mln/core/p_array_piter.hh (revision 1563) @@ -79,7 +79,7 @@ protected: const std::vector<P>& vect_; - unsigned i_; + unsigned i_; // FIXME: Why it's unsigned in fwd iterator and signed in the bkd one ? P p_; }; Index: trunk/milena/mln/util/lazy_set.hh =================================================================== --- trunk/milena/mln/util/lazy_set.hh (revision 0) +++ trunk/milena/mln/util/lazy_set.hh (revision 1563) @@ -0,0 +1,320 @@ +// Copyright (C) 2007 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 51 Franklin Street, Fifth Floor, +// Boston, MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef MLN_UTIL_LAZY_SET_HH +# define MLN_UTIL_LAZY_SET_HH + +/*! \file mln/core/internal/lazy_set.hh + * + * \brief Definition of mln::lazy_set_ for internal use only. + */ + +# include <vector> +# include <set> +# include <iterator> +# include <algorithm> + +# include <mln/core/internal/force_exact.hh> + + +namespace mln +{ + + namespace util + { + + /*! \brief An "efficient" mathematical set class. + * + * \internal + * + * This set class is designed to store a mathematical set and to + * present it to the user as a linear array (std::vector). + * + * Elements are stored by copy. Implementation is lazy. + * + * \invariant \a v_.size() == s_.size() + * + * The parameter \c E is the element type, which shall not be + * const-qualified. + * + * \todo Add a remove method. + */ + template <typename E> + class lazy_set_ + { + public: + + /// Type of the stored value. + typedef E value; + + /*! \brief Insert an element \p elt into the set. + * + * \param[in] elt The element to be inserted. + * + * If \p elt is already in the set, this method is a no-op. + * + * \return The set itself after insertion. + */ + lazy_set_<E>& insert(const E& elt); + + + /*! \brief Remove an element \p elt into the set. + * + * \param[in] elt The element to be inserted. + * + * If \p elt is already in the set, this method is a no-op. + * + * \return The set itself after suppression. + */ + lazy_set_<E>& remove(const E& elt); + + + /*! \brief Return the i-th element of the set. + * + * \param[in] i Index of the element to retrieve. + * + * \pre i < nelements() + * + * The element is returned by reference and is constant. + */ + const E& element(unsigned i) const; + + /*! \brief Return the i-th element of the set. + * + * \param[in] i Index of the element to retrieve. + * + * \pre i < nelements() + * + * The element is returned by reference and is constant. + */ + const E& operator[](unsigned i) const; + + /*! \brief Return the number of elements of the set. + */ + unsigned nelements() const; + + + /*! \brief Test if the object \p elt belongs to the set. + * + * \param[in] elt A possible element of the set. + * + * \return True is \p elt is in the set. + */ + bool has(const E& elt) const; + + + /*! \brief Test if the set is empty. + */ + bool is_empty() const; + + + /*! \brief Make the set empty. + * + * All elements contained in the set are destroyed so the set is + * emptied. + * + * \post is_empty() == true + */ + void clear(); + + + /*! \brief Give access to the set elements. + * + * The complexity of this method is O(1). + * + * \return An array (std::vector) of elements. + */ + const std::vector<E>& vect() const; + + /// Constructor without arguments. + lazy_set_(); + + private: + + /*! \brief Array of elements. + * + * This structure is only updated (if required) when elements + * are accessed. + */ + mutable std::vector<E> v_; + + /*! \brief Set of elements. + * + * This structure is always up-to-date w.r.t. the set contents. + */ + std::set<E> s_; + + + /*! \brief Update \a v_ from \a s_. + * + * FIXME: explain. + */ + void update_() const; + + /// Tell if \a v_ needs to be updated. + mutable bool needs_update_; + }; + + + /*! \brief Print a set \p s into the output stream \p + * ostr. + * + * \param[in,out] ostr An output stream. + * \param[in] s A set. + * + * \return The modified output stream \p ostr. + * + * \relates mln::internal::lazy_set_ + */ + // FIXME : ambiguous with point_set operator << + // template <typename E> + // std::ostream& operator<<(std::ostream& ostr, const lazy_set_<E>& s); + + + +# ifndef MLN_INCLUDE_ONLY + + template <typename E> + lazy_set_<E>::lazy_set_() + { + needs_update_ = false; + } + + template <typename E> + lazy_set_<E>& + lazy_set_<E>::insert(const E& elt) + { + s_.insert(elt); + if (needs_update_ == false) + needs_update_ = true; + return internal::force_exact< lazy_set_<E> >(*this); + } + + template <typename E> + lazy_set_<E>& + lazy_set_<E>::remove(const E& elt) + { + // FIXME : doesn't compile + std::remove(s_.begin(), s_.end(), elt); + if (needs_update_ == false) + needs_update_ = true; + return internal::force_exact< lazy_set_<E> >(*this); + } + + template <typename E> + const E& + lazy_set_<E>::element(unsigned i) const + { + assert(i < v_.size()); + if (needs_update_) + update_(); + return v_[i]; + } + + template <typename E> + const E& + lazy_set_<E>::operator[](unsigned i) const + { + return this->element(i); + } + + template <typename E> + unsigned + lazy_set_<E>::nelements() const + { + if (needs_update_) + update_(); + return v_.size(); + } + + template <typename E> + bool + lazy_set_<E>::has(const E& elt) const + { + return s_.find(elt) != s_.end(); + } + + template <typename E> + bool + lazy_set_<E>::is_empty() const + { + return nelements() == 0; + } + + template <typename E> + void + lazy_set_<E>::clear() + { + v_.clear(); + s_.clear(); + needs_update_ = false; + mln_postcondition(is_empty()); + } + + template <typename E> + const std::vector<E>& + lazy_set_<E>::vect() const + { + if (needs_update_) + update_(); + return v_; + } + + template <typename E> + void + lazy_set_<E>::update_() const + { + mln_precondition(needs_update_); + v_.clear(); + std::copy(s_.begin(), s_.end(), std::back_inserter(v_)); + // no s_.clear() here: + // - we want to keep information up-to-date in s_ + // - we want to save some execution time + needs_update_ = false; + } + + // FIXME : ambiguous with point_set operator << + // template <typename E> + // std::ostream& operator<<(std::ostream& ostr, + // const lazy_set_<E>& s) + // { + // ostr << '['; + // const unsigned n = s.nelements(); + // for (unsigned i = 0; i < n; ++i) + // ostr << s.element(i) + // << (i == s.nelements() - 1 ? ']' : ','); + // return ostr; + // } + +# endif // ! MLN_INCLUDE_ONLY + + } // end of namespace mln::util + +} // end of namespace mln + + +#endif // ! MLN_UTIL_LAZY_SET_HH
participants (1)
-
nivaul_s@lrde.epita.fr