
URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox ChangeLog: 2009-03-01 Frederic Bour <bour@lrde.epita.fr> Add exo3 "assignable" functions part, correct other parts. * rush/exo3/Makefile: Added assignable target. * rush/exo3/assignable.cc: New. * rush/exo3/assignable.cc: New. * rush/exo3/exo3_1.cc: Renamed to... * rush/exo3/exo3_fun_area.cc: New. * rush/exo3/exo3_2.cc: Renamed to... * rush/exo3/exo3_fun_generator.cc: New. --- Makefile | 10 - assignable.cc | 251 ++++++++++++++++++++++++++++++++++ exo3_fun_area.cc | 313 ++++++++++++++++++++++++++++++++++++++++++ exo3_fun_generator.cc | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 935 insertions(+), 5 deletions(-) Index: trunk/milena/sandbox/theo/rush/exo3/exo3_1.cc (deleted) =================================================================== Index: trunk/milena/sandbox/theo/rush/exo3/exo3_2.cc (deleted) =================================================================== Index: trunk/milena/sandbox/theo/rush/exo3/exo3_fun_generator.cc =================================================================== --- trunk/milena/sandbox/theo/rush/exo3/exo3_fun_generator.cc (revision 0) +++ trunk/milena/sandbox/theo/rush/exo3/exo3_fun_generator.cc (revision 3452) @@ -0,0 +1,366 @@ +#include <mln/core/image/image2d.hh> +#include <mln/core/concept/function.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/var.hh> +#include <mln/value/int_u8.hh> +#include <mln/pw/all.hh> +#include <mln/io/pgm/all.hh> +#include <mln/io/pbm/all.hh> +#include <mln/trait/all.hh> +#include <mln/fun/internal/resolve.hh> +#include <mln/morpho/attribute/card.hh> +#include <mln/morpho/attribute/sharpness.hh> +#include <mln/level/sort_offsets.hh> +#include <mln/canvas/morpho/attribute_filter.hh> +#include <mln/canvas/morpho/internal/find_root.hh> + +namespace exo3 +{ + // Se code est un peu ... sale, et tente de répondre aux problèmes + // techniques apparus lors de la présentation (bonus ?). + + // mln::trait::solve se spécialise pour des template <class T> + // en premier paramètre. + // Afin de transmettre plusieurs types, on passe par + // T = couple<TExact, TAux>. On 'hérite' de la catégorie de TExact. + // Utiliser pour le générateur de (méta)fonctions. + template <typename TExact, typename TAux> + struct couple + { + typedef TExact Exact; + typedef typename mln::category<Exact>::ret category; + + typedef TAux Aux; + }; + + // Crée un type unique vide pour un type donné, évite une instantation ?! + // (inutile probablement, dans le doute...). + template <typename U> + struct uniq + { + }; + + // Get attribute from attribute "E" exact. + template <typename E> + typename E::result + get_attribute(uniq<E>, const E& exact) + { + return exact.to_result(); + } + + // area + template <typename I, typename E> + unsigned get_attribute(uniq< mln::morpho::attribute::card<I> >, const E& exact) + { + return exact.area(); + } + + // volume + template <typename I, typename E> + unsigned get_attribute(uniq< mln::morpho::attribute::volume<I> >, const E& exact) + { + return exact.volume(); + } +} + +namespace mln +{ + namespace fun + { + namespace v2v + { + template <typename T> struct from_accu_t; + template <typename T> struct solve_from_accu; + } + + namespace meta + { + template <template <class> class A> + struct from_accu + { + template <typename T> + typename fun::v2v::from_accu_t< exo3::couple<T, A<T> > >::result + operator()(const T& t1) const + { + fun::v2v::from_accu_t< exo3::couple<T, A<T> > > f; + return f(t1); + } + + template <typename T> + struct with + { + typedef fun::v2v::from_accu_t< exo3::couple<T, A<T> > > ret; + }; + }; + } + } + + namespace trait + { + template <typename TE, typename TA> + struct set_unary_< fun::v2v::from_accu_t, Accumulator, exo3::couple<TE, TA> > + { + typedef set_unary_< fun::v2v::from_accu_t, Accumulator, exo3::couple<TE, TA> > ret; + typedef unsigned result; + + static + result + exec(const TE& t1) + { + return exo3::get_attribute(exo3::uniq<TA>(), t1); + } + }; + + } // mln::trait + + namespace fun + { + namespace v2v + { + template <typename T> + struct from_accu_t; + + template <typename TE, typename TA> + struct from_accu_t< exo3::couple<TE, TA> > : mln::Function_v2v< from_accu_t< exo3::couple<TE, TA> > > + { + typedef mln_fun_internal_resolve(from_accu_t) impl; + typedef typename impl::result result; + + result + operator()(const TE& t1) const + { + return impl::exec(t1); + } + }; + + template <typename A> + struct solve_from_accu + { + // FIXME: Not sure whether inheritance is the right way... + // But templated typedef are not currently available. + template <typename E> + struct ret : from_accu_t< exo3::couple< E, A > > + { + }; + }; + } + } + +} + +namespace exo3 +{ + template <typename F, typename G> + struct functors_compose_ + : mln::Function_p2v< functors_compose_<F, G> > + { + typedef typename F::result result; + + functors_compose_() {} + functors_compose_(const F& f, const G& g) + : f_(f), g_(g) + { + } + + template <typename T> + result + operator()(const T& p) const + { + return f_(g_(p)); + } + + protected: + F f_; + G g_; + }; + +} + +namespace mln +{ + + namespace trait + { + + template <typename TG, typename TA> + struct set_unary_<fun::v2v::from_accu_t, mln::Function_p2v, exo3::couple<TG, TA> > + { + typedef set_unary_<fun::v2v::from_accu_t, mln::Function_p2v, exo3::couple<TG, TA> > ret; + typedef exo3::functors_compose_<fun::v2v::from_accu_t< exo3::couple<typename TG::result, TA> >, TG> result; + + static + result + exec(const TG& g) + { + return exo3::functors_compose_<fun::v2v::from_accu_t< exo3::couple<typename TG::result, TA> >, TG> + (fun::v2v::from_accu_t<exo3::couple<typename TG::result, TA> >(), g); + } + }; + } + +} + +// Modified copy of mln::canvas::morpho::attribute_filter +namespace exo3_filter +{ + using namespace mln; + + template <typename I, typename N, typename S, typename A> + mln_ch_value(I, A) + attribute_filter (const Image<I>& input_, + const Neighborhood<N>& nbh_, + const Site_Set<S>& s_, + const Accumulator<A>& a_, + const typename A::result& lambda) + { + const I& input = exact(input_); + const N& nbh = exact(nbh_); + const S& s = exact(s_); + (void)a_; // To avoid warning at compilation + + // Local type. + typedef mln_psite(I) P; + + mln_ch_value(I, bool) deja_vu; + mln_ch_value(I, bool) activity; + mln_ch_value(I, P) parent; + mln_ch_value(I, A) data; + + { + initialize(deja_vu, input); + data::fill(deja_vu, false); + initialize(activity, input); + data::fill(activity, true); + initialize(parent, input); + initialize(data, input); + } + { + mln_fwd_piter(S) p(s); + mln_niter(N) n(nbh, p); + + for_all(p) + { + { + parent(p) = p; + mln::canvas::morpho::impl::take_as_init (data(p), input, p); + } + + for_all(n) + if (input.domain().has(n) && deja_vu(n)) + { + P r = mln::canvas::morpho::impl::generic::find_root(parent, n); + if (r != p) + { + if (input(r) == input(p) || (activity(r) && (data(r) < lambda))) + { + data(p).take(data(r)); + parent(r) = p; + if (activity(r) == false) + activity(p) = false; + } + else + { + activity(p) = false; + } + } + } + deja_vu(p) = true; + } + } + // Second pass. + { + mln_bkd_piter(S) p(s); + for_all(p) + if (parent(p) != p) + data(p) = data(parent(p)); + } + return data; + } + + // Surcharger les operateurs uniquement pour les builtins... + // l'idée est de provoquer une sfinae. J'ai pas trouvé mieux :( + // (pas vu d'opérateurs <, >, ... pour les fonctions utilisant mln::trait::solve) + + // C is Category, R is return type + template <typename C, typename R> + struct ret_builtin; + template <typename T, typename R> + struct ret_builtin< value::Built_In< T > , R> + { + typedef R ret; + }; + +} // end of namespace exo3_filter + +#define templatize(...) __VA_ARGS__ + +#define op_for_cst(name, op) \ + template <typename F, typename C> \ + mln_trait_op_##name( \ + F, \ + templatize(typename exo3_filter::ret_builtin< typename mln::category< C >::ret, mln::pw::cst_<C> >::ret) \ + ) \ + operator op (const mln::Function_p2v<F>& f, const C& c) \ + { \ + return exact(f) op mln::pw::cst(c); \ + } \ + \ + template <typename F, typename C> \ + mln_trait_op_##name( \ + templatize(typename exo3_filter::ret_builtin< typename mln::category< C >::ret, mln::pw::cst_<C> >::ret), \ + F \ + ) \ + operator op (const C& c, const mln::Function_p2v<F>& f) \ + { \ + return mln::pw::cst(c) or exact(f); \ + } + +op_for_cst(less, <) +op_for_cst(greater, >) +op_for_cst(leq, <=) +op_for_cst(geq, >=) +op_for_cst(eq, ==) +op_for_cst(neq, !=) + +#undef op_for_cst + +void usage(char* argv[]) +{ + std::cerr << "usage: " << argv[0] << " input.pgm sharpness minvol minarea (inclusive or)" + << std::endl; + abort(); +} + +int main(int argc, char **argv) +{ + using namespace mln; + using value::int_u8; + + typedef image2d<int_u8> I; + + float l_sharpness; + unsigned l_minvol; + unsigned l_minarea; + I input; + + if (argc < 5) + usage(argv); + + io::pgm::load(input, argv[1]); + + l_sharpness = atof(argv[2]); + l_minvol = atoi(argv[3]); + l_minarea = atoi(argv[4]); + + mln_VAR(sharp, exo3_filter::attribute_filter(input, c4(), + level::sort_psites_decreasing(input), + morpho::attribute::sharpness<I>(), l_sharpness)); + + mln_VAR(s, pw::value(sharp)); + + mln::fun::meta::from_accu< mln::morpho::attribute::volume > vol; + mln::fun::meta::from_accu< mln::morpho::attribute::card > area; + + io::pbm::save((vol(s) > l_minvol || area(s) > l_minarea) | input.domain(), "out.pbm"); + +} \ No newline at end of file Index: trunk/milena/sandbox/theo/rush/exo3/exo3_fun_area.cc =================================================================== --- trunk/milena/sandbox/theo/rush/exo3/exo3_fun_area.cc (revision 0) +++ trunk/milena/sandbox/theo/rush/exo3/exo3_fun_area.cc (revision 3452) @@ -0,0 +1,313 @@ +#include <mln/core/image/image2d.hh> +#include <mln/core/concept/function.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/var.hh> +#include <mln/value/int_u8.hh> +#include <mln/pw/all.hh> +#include <mln/io/pgm/all.hh> +#include <mln/io/pbm/all.hh> +#include <mln/trait/all.hh> +#include <mln/fun/internal/resolve.hh> +#include <mln/morpho/attribute/card.hh> +#include <mln/morpho/attribute/sharpness.hh> +#include <mln/level/sort_offsets.hh> +#include <mln/canvas/morpho/attribute_filter.hh> +#include <mln/canvas/morpho/internal/find_root.hh> + +namespace mln +{ + namespace fun + { + namespace v2v + { + template <typename T> struct area; + } + + /// Meta function for area calculation + namespace meta + { + + struct area + { + template <typename T> + typename fun::v2v::area<T>::result + operator()(const T& t1) const + { + fun::v2v::area<T> f; + return f(t1); + } + + template <typename T> + struct with + { + typedef fun::v2v::area<T> ret; + }; + }; + + } // end of namespace mln::fun::meta + + /// Function for area calculation + namespace v2v + { + + template <typename T> + struct area : mln::Function_v2v< area<T> > + { + typedef mln_fun_internal_resolve(area) impl; + typedef typename impl::result result; + + result + operator()(const T& t1) const + { + return impl::exec(t1); + } + }; + + } // end of namespace mln::fun::v2v + + /// Function_p2v composition + namespace p2v + { + template <typename F, typename G> + struct composition_t + : mln::Function_p2v< composition_t<F, G> > + { + typedef typename F::result result; + + composition_t() {} + composition_t(const F& f, const G& g) + : f_(f), g_(g) + { + } + + template <typename T> + result + operator()(const T& p) const + { + return f_(g_(p)); + } + + protected: + F f_; + G g_; + }; + + } // end of namespace mln::fun::p2v + + } // end of namespace mln::fun + + /// fun::v2v::area trait for composition + namespace trait + { + template <typename T> + struct set_unary_< fun::v2v::area, Accumulator, T > + { + typedef set_unary_< fun::v2v::area, Accumulator, T > ret; + typedef unsigned result; + + static + result + exec(const T& t1) + { + return t1.area(); + } + }; + + template <typename I> + struct set_precise_unary_< fun::v2v::area, mln::morpho::attribute::card<I> > + { + typedef set_precise_unary_< fun::v2v::area, mln::morpho::attribute::card<I> > ret; + typedef unsigned result; + + static + result + exec(const mln::morpho::attribute::card<I>& t1) + { + return t1.to_result(); + } + }; + + template <typename G> + struct set_unary_<fun::v2v::area, mln::Function_p2v, G> + { + typedef set_unary_<fun::v2v::area, mln::Function_p2v, G> ret; + typedef fun::p2v::composition_t<fun::v2v::area<typename G::result>, G> result; + + static + result + exec(const G& g) + { + return fun::p2v::composition_t<fun::v2v::area<typename G::result>, G> + (fun::v2v::area<typename G::result>(), g); + } + }; + } + +} + +// Modified copy of mln::canvas::morpho::attribute_filter +// for purpose of testing. +namespace exo3_filter +{ + using namespace mln; + + template <typename I, typename N, typename S, typename A> + mln_ch_value(I, A) + attribute_filter (const Image<I>& input_, + const Neighborhood<N>& nbh_, + const Site_Set<S>& s_, + const Accumulator<A>& a_, + const typename A::result& lambda) + { + const I& input = exact(input_); + const N& nbh = exact(nbh_); + const S& s = exact(s_); + (void)a_; // To avoid warning at compilation + + // Local type. + typedef mln_psite(I) P; + + mln_ch_value(I, bool) deja_vu; + mln_ch_value(I, bool) activity; + mln_ch_value(I, P) parent; + mln_ch_value(I, A) data; + + { + initialize(deja_vu, input); + data::fill(deja_vu, false); + initialize(activity, input); + data::fill(activity, true); + initialize(parent, input); + initialize(data, input); + } + { + mln_fwd_piter(S) p(s); + mln_niter(N) n(nbh, p); + + for_all(p) + { + { + parent(p) = p; + mln::canvas::morpho::impl::take_as_init (data(p), input, p); + } + + for_all(n) + if (input.domain().has(n) && deja_vu(n)) + { + P r = mln::canvas::morpho::impl::generic::find_root(parent, n); + if (r != p) + { + if (input(r) == input(p) || (activity(r) && (data(r) < lambda))) + { + data(p).take(data(r)); + parent(r) = p; + if (activity(r) == false) + activity(p) = false; + } + else + { + activity(p) = false; + } + } + } + deja_vu(p) = true; + } + } + // Second pass. + { + mln_bkd_piter(S) p(s); + for_all(p) + if (parent(p) != p) + data(p) = data(parent(p)); + } + return data; + } +} // end of namespace exo3_filter + +namespace exo3_filter +{ + // Le but est de surcharger les operateurs uniquement pour les Built_In<>... + // l'idée est de provoquer une sfinae. J'ai pas trouvé mieux :( + // (pas vu d'opérateurs <, >, ... pour les Function<> utilisant mln::trait::solve) + + // C is Category, R is return type + template <typename C, typename R> + struct ret_builtin; + + template <typename T, typename R> + struct ret_builtin< value::Built_In< T > , R> + { + typedef R ret; + }; + +} // end of namespace exo3_filter + +#define templatize(...) __VA_ARGS__ + +#define op_for_cst(name, op) \ + template <typename F, typename C> \ + mln_trait_op_##name( \ + F, \ + templatize(typename exo3_filter::ret_builtin< typename mln::category< C >::ret, mln::pw::cst_<C> >::ret) \ + ) \ + operator op (const mln::Function_p2v<F>& f, const C& c) \ + { \ + return exact(f) op mln::pw::cst(c); \ + } \ + \ + template <typename F, typename C> \ + mln_trait_op_##name( \ + templatize(typename exo3_filter::ret_builtin< typename mln::category< C >::ret, mln::pw::cst_<C> >::ret), \ + F \ + ) \ + operator op (const C& c, const mln::Function_p2v<F>& f) \ + { \ + return mln::pw::cst(c) or exact(f); \ + } + +op_for_cst(less, <) +op_for_cst(greater, >) +op_for_cst(leq, <=) +op_for_cst(geq, >=) +op_for_cst(eq, ==) +op_for_cst(neq, !=) + +#undef op_for_cst + +void usage(char* argv[]) +{ + std::cerr << "usage: " << argv[0] << " input.pgm sharpness minarea maxarea" + << std::endl; + abort(); +} + +int main(int argc, char **argv) +{ + using namespace mln; + using value::int_u8; + + typedef image2d<int_u8> I; + + float l_sharpness; + unsigned l_minarea; + unsigned l_maxarea; + I input; + + if (argc < 5) + usage(argv); + + io::pgm::load(input, argv[1]); + + l_sharpness = atof(argv[2]); + l_minarea = atoi(argv[3]); + l_maxarea = atoi(argv[4]); + + mln_VAR(sharp, exo3_filter::attribute_filter(input, c4(), + level::sort_psites_decreasing(input), + morpho::attribute::sharpness<I>(), l_sharpness)); + + mln_VAR(s, pw::value(sharp)); + + mln::fun::meta::area area; + io::pbm::save((area(s) > l_minarea && area(s) < l_maxarea) | input.domain(), "out.pbm"); +} \ No newline at end of file Index: trunk/milena/sandbox/theo/rush/exo3/assignable.cc =================================================================== --- trunk/milena/sandbox/theo/rush/exo3/assignable.cc (revision 0) +++ trunk/milena/sandbox/theo/rush/exo3/assignable.cc (revision 3452) @@ -0,0 +1,251 @@ +#include <mln/core/concept/all.hh> +#include <mln/fun/all.hh> +#include <mln/trait/all.hh> +#include <mln/fun/meta/green.hh> +#include <mln/fun/v2w2v/cos.hh> +#include <mln/fun/v2w_w2v/norm.hh> + +// Main definition, use solve_unary to find "implementation" +// Usage: assignable< mln::fun::v2w2v::cos<double > cos; +template <typename F> +struct assignable : mln::trait::solve_unary<assignable, F>::ret +{ +}; + +// Meta for assignable +// Usage: meta::assignable< mln::fun::v2w2v::cos > meta_cos; +namespace meta +{ + template <template <class> class T> + struct assignable + { + template <typename V> + struct with + { + typedef ::assignable< T<V> > ret; + }; + + template <typename V> + typename with<V>::ret::result + operator() (const V& value) const + { + return typename with<V>::ret()(value); + } + + template <typename V> + typename with<V>::ret::lresult + operator() (V& value) const + { + return typename with<V>::ret()(value); + } + }; + + template <template <class, class> class T> + struct assignable_bin + { + template <typename V, typename R> + struct with + { + typedef ::assignable< T<V, R> > ret; + }; + }; +}; + +// Tricks to play with templates +namespace tricky +{ + // Disgusting: used to subsitute only according to certain condition (sfinae) + template <typename C1, typename C2 = void, + typename C3 = void, typename C4 = void> + struct subst_test + { + typedef void ret; + }; + + // Reinstantiate a template with another parameter + template <typename Tmpl, typename E> + struct reinstantiate; + + template <template <class> class Tmpl, typename T, typename E> + struct reinstantiate<Tmpl<T>, E> + { + typedef Tmpl<E> ret; + }; + + template <typename T, template <class> class> + struct if_is_templated; + + template <template <class> class Tmpl, typename M> + struct if_is_templated< Tmpl<M>, Tmpl > + { + }; +} + +// Internal namespace for assignable functor +namespace internal +{ + namespace assignable + { + // + template <typename F, typename X, typename V> + void + compute_inv(const mln::Function_v2w_w2v<F>& f, X& x, const V& v) + { + x = mln::exact(f).f_1(v, x); + }; + + template <typename F, typename X, typename V> + void + compute_inv(const mln::Function_v2w2v<F>& f, X& x, const V& v) + { + x = mln::exact(f).f_1(v); + }; + + template <typename F, typename X> + struct modifier + { + modifier(X& x, const F& f) + : x_(&x), f_(&f) + { + } + + operator const X& () const + { return *x_; }; + + operator X& () + { return *x_; }; + + template <typename V> + const V& operator= (const V& v) + { + compute_inv(*f_, *x_, v); + return v; + } + + private: + X *x_; + const F* f_; + }; + + // Solve modifiable type: do we need a modifier or can we directly set thru lvalue ? + template <typename F, typename V> + struct solve_value + { + typedef typename F::result ret; + }; + + template <typename F> + struct solve_value<F, typename tricky::subst_test< typename F::value >::ret > + { + typedef typename F::value ret; + }; + + // Solve modifiable type: do we need a modifier or can we directly set thru lvalue ? + template <typename F, typename Dummy> + struct solve_lresult + { + typedef modifier<F, typename F::result> ret; + + template <typename V> + static inline + ret + modify(V& value, const F& thru) + { + return ret(value, thru); + } + }; + + template <typename F> + struct solve_lresult<F, typename tricky::subst_test< tricky::if_is_templated<typename F::F, mln::function> >::ret > + { + typedef typename F::lresult ret; + + template <typename X> + static inline + ret + modify(X& value, const F& thru) + { + (void)thru; + return mln::function<F>().write(value); + } + }; + + template <typename F> + struct function_assign : public tricky::reinstantiate<typename F::category, function_assign<F> >::ret + { + typedef F function; + typedef typename function::result result; + typedef typename solve_value<function, void>::ret value; + typedef solve_lresult<function, void> lresult_solve; + typedef typename lresult_solve::ret lresult; + + inline + result + operator() (const value& t) const + { + return f_.read(t); + } + + inline + lresult + operator() (value& t) + { + return lresult_solve::modify(t, f_); + } + + protected: + F f_; + }; + } +} + +namespace mln +{ + + namespace trait + { + + template <typename F> + struct set_unary_< ::assignable, Function_v2w2v, F > + { + typedef ::internal::assignable::function_assign<F> ret; + }; + + template <typename F> + struct set_unary_< ::assignable, Function_v2w_w2v, F > + { + typedef ::internal::assignable::function_assign<F> ret; + }; + + }; + +}; + +#define print_dbg(expr) std::cout << #expr "\t\t == " << (expr) << std::endl +int main() +{ + mln::value::rgb8 p; + double x; + + assignable< mln::fun::v2w2v::cos<double> > cos; + assignable< mln::meta::red<mln::value::rgb8> > red; + assignable< mln::meta::green<mln::value::rgb8> > green; + assignable< mln::meta::blue<mln::value::rgb8> > blue; + + meta::assignable< mln::fun::v2w2v::cos > meta_cos; + meta::assignable< mln::meta::green > meta_green; + + print_dbg(cos(x) = 0); + print_dbg(x); + print_dbg(meta_cos(x) = 1); + print_dbg(x); + + print_dbg(red(p) = 0); + print_dbg(green(p) = 127); + print_dbg(blue(p) = 255); + print_dbg(p); + + print_dbg(meta_green(p) = 0); + print_dbg(p); + +}; \ No newline at end of file Index: trunk/milena/sandbox/theo/rush/exo3/Makefile =================================================================== --- trunk/milena/sandbox/theo/rush/exo3/Makefile (revision 3451) +++ trunk/milena/sandbox/theo/rush/exo3/Makefile (revision 3452) @@ -1,9 +1,9 @@ -TARGETS=exo3_1 exo3_2 inverse +TARGETS=exo3_fun_area exo3_fun_generator assignable OLENADIR=../../../../.. MILENADIR=$(OLENADIR)/milena -CXXFLAGS=-I$(MILENADIR) -I./ -W -Wall -O2 +CXXFLAGS+=-I$(MILENADIR) -I./ -W -Wall -O2 CXX=g++ LD=g++ LDFLAGS= @@ -11,9 +11,9 @@ all: $(TARGETS) -exo3_1: exo3_1.o -exo3_2: exo3_2.o -inverse: inverse.o +exo3_fun_area: exo3_fun_area.o +exo3_fun_generator: exo3_fun_generator.o +assignable: assignable.o $(TARGETS): $(LD) $(LDFLAGS) -o $@ $<