Chris@16
|
1 ///////////////////////////////////////////////////////////////////////////////
|
Chris@16
|
2 // depends_on.hpp
|
Chris@16
|
3 //
|
Chris@16
|
4 // Copyright 2005 Eric Niebler. Distributed under the Boost
|
Chris@16
|
5 // Software License, Version 1.0. (See accompanying file
|
Chris@16
|
6 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
7
|
Chris@16
|
8 #ifndef BOOST_ACCUMULATORS_FRAMEWORK_DEPENDS_ON_HPP_EAN_28_10_2005
|
Chris@16
|
9 #define BOOST_ACCUMULATORS_FRAMEWORK_DEPENDS_ON_HPP_EAN_28_10_2005
|
Chris@16
|
10
|
Chris@16
|
11 #include <boost/version.hpp>
|
Chris@16
|
12 #include <boost/mpl/end.hpp>
|
Chris@16
|
13 #include <boost/mpl/map.hpp>
|
Chris@16
|
14 #include <boost/mpl/set.hpp>
|
Chris@16
|
15 #include <boost/mpl/copy.hpp>
|
Chris@16
|
16 #include <boost/mpl/fold.hpp>
|
Chris@16
|
17 #include <boost/mpl/size.hpp>
|
Chris@16
|
18 #include <boost/mpl/sort.hpp>
|
Chris@16
|
19 #include <boost/mpl/insert.hpp>
|
Chris@16
|
20 #include <boost/mpl/assert.hpp>
|
Chris@16
|
21 #include <boost/mpl/remove.hpp>
|
Chris@16
|
22 #include <boost/mpl/vector.hpp>
|
Chris@16
|
23 #include <boost/mpl/inherit.hpp>
|
Chris@16
|
24 #include <boost/mpl/identity.hpp>
|
Chris@16
|
25 #include <boost/mpl/equal_to.hpp>
|
Chris@16
|
26 #include <boost/mpl/contains.hpp>
|
Chris@16
|
27 #include <boost/mpl/transform.hpp>
|
Chris@16
|
28 #include <boost/mpl/is_sequence.hpp>
|
Chris@16
|
29 #include <boost/mpl/placeholders.hpp>
|
Chris@16
|
30 #include <boost/mpl/insert_range.hpp>
|
Chris@16
|
31 #include <boost/mpl/back_inserter.hpp>
|
Chris@16
|
32 #include <boost/mpl/transform_view.hpp>
|
Chris@16
|
33 #include <boost/mpl/inherit_linearly.hpp>
|
Chris@16
|
34 #include <boost/type_traits/is_base_and_derived.hpp>
|
Chris@16
|
35 #include <boost/preprocessor/repetition/repeat.hpp>
|
Chris@16
|
36 #include <boost/preprocessor/repetition/enum_params.hpp>
|
Chris@16
|
37 #include <boost/preprocessor/facilities/intercept.hpp>
|
Chris@16
|
38 #include <boost/accumulators/accumulators_fwd.hpp>
|
Chris@16
|
39 #include <boost/fusion/include/next.hpp>
|
Chris@16
|
40 #include <boost/fusion/include/equal_to.hpp>
|
Chris@16
|
41 #include <boost/fusion/include/value_of.hpp>
|
Chris@16
|
42 #include <boost/fusion/include/mpl.hpp>
|
Chris@16
|
43 #include <boost/fusion/include/end.hpp>
|
Chris@16
|
44 #include <boost/fusion/include/begin.hpp>
|
Chris@16
|
45 #include <boost/fusion/include/cons.hpp>
|
Chris@16
|
46
|
Chris@16
|
47 namespace boost { namespace accumulators
|
Chris@16
|
48 {
|
Chris@16
|
49 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
50 // as_feature
|
Chris@16
|
51 template<typename Feature>
|
Chris@16
|
52 struct as_feature
|
Chris@16
|
53 {
|
Chris@16
|
54 typedef Feature type;
|
Chris@16
|
55 };
|
Chris@16
|
56
|
Chris@16
|
57 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
58 // weighted_feature
|
Chris@16
|
59 template<typename Feature>
|
Chris@16
|
60 struct as_weighted_feature
|
Chris@16
|
61 {
|
Chris@16
|
62 typedef Feature type;
|
Chris@16
|
63 };
|
Chris@16
|
64
|
Chris@16
|
65 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
66 // feature_of
|
Chris@16
|
67 template<typename Feature>
|
Chris@16
|
68 struct feature_of
|
Chris@16
|
69 {
|
Chris@16
|
70 typedef Feature type;
|
Chris@16
|
71 };
|
Chris@16
|
72
|
Chris@16
|
73 namespace detail
|
Chris@16
|
74 {
|
Chris@16
|
75 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
76 // feature_tag
|
Chris@16
|
77 template<typename Accumulator>
|
Chris@16
|
78 struct feature_tag
|
Chris@16
|
79 {
|
Chris@16
|
80 typedef typename Accumulator::feature_tag type;
|
Chris@16
|
81 };
|
Chris@16
|
82
|
Chris@16
|
83 template<typename Feature>
|
Chris@16
|
84 struct undroppable
|
Chris@16
|
85 {
|
Chris@16
|
86 typedef Feature type;
|
Chris@16
|
87 };
|
Chris@16
|
88
|
Chris@16
|
89 template<typename Feature>
|
Chris@16
|
90 struct undroppable<tag::droppable<Feature> >
|
Chris@16
|
91 {
|
Chris@16
|
92 typedef Feature type;
|
Chris@16
|
93 };
|
Chris@16
|
94
|
Chris@16
|
95 // For the purpose of determining whether one feature depends on another,
|
Chris@16
|
96 // disregard whether the feature is droppable or not.
|
Chris@16
|
97 template<typename A, typename B>
|
Chris@16
|
98 struct is_dependent_on
|
Chris@16
|
99 : is_base_and_derived<
|
Chris@16
|
100 typename feature_of<typename undroppable<B>::type>::type
|
Chris@16
|
101 , typename undroppable<A>::type
|
Chris@16
|
102 >
|
Chris@16
|
103 {};
|
Chris@16
|
104
|
Chris@16
|
105 template<typename Feature>
|
Chris@16
|
106 struct dependencies_of
|
Chris@16
|
107 {
|
Chris@16
|
108 typedef typename Feature::dependencies type;
|
Chris@16
|
109 };
|
Chris@16
|
110
|
Chris@16
|
111 // Should use mpl::insert_range, but doesn't seem to work with mpl sets
|
Chris@16
|
112 template<typename Set, typename Range>
|
Chris@16
|
113 struct set_insert_range
|
Chris@16
|
114 : mpl::fold<
|
Chris@16
|
115 Range
|
Chris@16
|
116 , Set
|
Chris@16
|
117 , mpl::insert<mpl::_1, mpl::_2>
|
Chris@16
|
118 >
|
Chris@16
|
119 {};
|
Chris@16
|
120
|
Chris@16
|
121 template<typename Features>
|
Chris@16
|
122 struct collect_abstract_features
|
Chris@16
|
123 : mpl::fold<
|
Chris@16
|
124 Features
|
Chris@16
|
125 , mpl::set0<>
|
Chris@16
|
126 , set_insert_range<
|
Chris@16
|
127 mpl::insert<mpl::_1, feature_of<mpl::_2> >
|
Chris@16
|
128 , collect_abstract_features<dependencies_of<mpl::_2> >
|
Chris@16
|
129 >
|
Chris@16
|
130 >
|
Chris@16
|
131 {};
|
Chris@16
|
132
|
Chris@16
|
133 template<typename Features>
|
Chris@16
|
134 struct depends_on_base
|
Chris@16
|
135 : mpl::inherit_linearly<
|
Chris@16
|
136 typename mpl::sort<
|
Chris@16
|
137 typename mpl::copy<
|
Chris@16
|
138 typename collect_abstract_features<Features>::type
|
Chris@16
|
139 , mpl::back_inserter<mpl::vector0<> >
|
Chris@16
|
140 >::type
|
Chris@16
|
141 , is_dependent_on<mpl::_1, mpl::_2>
|
Chris@16
|
142 >::type
|
Chris@16
|
143 // Don't inherit multiply from a feature
|
Chris@16
|
144 , mpl::if_<
|
Chris@16
|
145 is_dependent_on<mpl::_1, mpl::_2>
|
Chris@16
|
146 , mpl::_1
|
Chris@16
|
147 , mpl::inherit<mpl::_1, mpl::_2>
|
Chris@16
|
148 >
|
Chris@16
|
149 >::type
|
Chris@16
|
150 {
|
Chris@16
|
151 };
|
Chris@16
|
152 }
|
Chris@16
|
153
|
Chris@16
|
154 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
155 /// depends_on
|
Chris@16
|
156 template<BOOST_PP_ENUM_PARAMS(BOOST_ACCUMULATORS_MAX_FEATURES, typename Feature)>
|
Chris@16
|
157 struct depends_on
|
Chris@16
|
158 : detail::depends_on_base<
|
Chris@16
|
159 typename mpl::transform<
|
Chris@16
|
160 mpl::vector<BOOST_PP_ENUM_PARAMS(BOOST_ACCUMULATORS_MAX_FEATURES, Feature)>
|
Chris@16
|
161 , as_feature<mpl::_1>
|
Chris@16
|
162 >::type
|
Chris@16
|
163 >
|
Chris@16
|
164 {
|
Chris@16
|
165 typedef mpl::false_ is_weight_accumulator;
|
Chris@16
|
166 typedef
|
Chris@16
|
167 typename mpl::transform<
|
Chris@16
|
168 mpl::vector<BOOST_PP_ENUM_PARAMS(BOOST_ACCUMULATORS_MAX_FEATURES, Feature)>
|
Chris@16
|
169 , as_feature<mpl::_1>
|
Chris@16
|
170 >::type
|
Chris@16
|
171 dependencies;
|
Chris@16
|
172 };
|
Chris@16
|
173
|
Chris@16
|
174 namespace detail
|
Chris@16
|
175 {
|
Chris@16
|
176 template<typename Feature>
|
Chris@16
|
177 struct matches_feature
|
Chris@16
|
178 {
|
Chris@16
|
179 template<typename Accumulator>
|
Chris@16
|
180 struct apply
|
Chris@16
|
181 : is_same<
|
Chris@16
|
182 typename feature_of<typename as_feature<Feature>::type>::type
|
Chris@16
|
183 , typename feature_of<typename as_feature<typename feature_tag<Accumulator>::type>::type>::type
|
Chris@16
|
184 >
|
Chris@16
|
185 {};
|
Chris@16
|
186 };
|
Chris@16
|
187
|
Chris@16
|
188 template<typename Features, typename Accumulator>
|
Chris@16
|
189 struct contains_feature_of
|
Chris@16
|
190 {
|
Chris@16
|
191 typedef
|
Chris@16
|
192 mpl::transform_view<Features, feature_of<as_feature<mpl::_> > >
|
Chris@16
|
193 features_list;
|
Chris@16
|
194
|
Chris@16
|
195 typedef
|
Chris@16
|
196 typename feature_of<typename feature_tag<Accumulator>::type>::type
|
Chris@16
|
197 the_feature;
|
Chris@16
|
198
|
Chris@16
|
199 typedef
|
Chris@16
|
200 typename mpl::contains<features_list, the_feature>::type
|
Chris@16
|
201 type;
|
Chris@16
|
202 };
|
Chris@16
|
203
|
Chris@16
|
204 // This is to work around a bug in early versions of Fusion which caused
|
Chris@16
|
205 // a compile error if contains_feature_of<List, mpl::_> is used as a
|
Chris@16
|
206 // predicate to fusion::find_if
|
Chris@16
|
207 template<typename Features>
|
Chris@16
|
208 struct contains_feature_of_
|
Chris@16
|
209 {
|
Chris@16
|
210 template<typename Accumulator>
|
Chris@16
|
211 struct apply
|
Chris@16
|
212 : contains_feature_of<Features, Accumulator>
|
Chris@16
|
213 {};
|
Chris@16
|
214 };
|
Chris@16
|
215
|
Chris@16
|
216 template<
|
Chris@16
|
217 typename First
|
Chris@16
|
218 , typename Last
|
Chris@16
|
219 , bool is_empty = fusion::result_of::equal_to<First, Last>::value
|
Chris@16
|
220 >
|
Chris@16
|
221 struct build_acc_list;
|
Chris@16
|
222
|
Chris@16
|
223 template<typename First, typename Last>
|
Chris@16
|
224 struct build_acc_list<First, Last, true>
|
Chris@16
|
225 {
|
Chris@101
|
226 typedef fusion::nil_ type;
|
Chris@16
|
227
|
Chris@16
|
228 template<typename Args>
|
Chris@101
|
229 static fusion::nil_
|
Chris@16
|
230 call(Args const &, First const&, Last const&)
|
Chris@16
|
231 {
|
Chris@101
|
232 return fusion::nil_();
|
Chris@16
|
233 }
|
Chris@16
|
234 };
|
Chris@16
|
235
|
Chris@16
|
236 template<typename First, typename Last>
|
Chris@16
|
237 struct build_acc_list<First, Last, false>
|
Chris@16
|
238 {
|
Chris@16
|
239 typedef
|
Chris@16
|
240 build_acc_list<typename fusion::result_of::next<First>::type, Last>
|
Chris@16
|
241 next_build_acc_list;
|
Chris@16
|
242
|
Chris@16
|
243 typedef fusion::cons<
|
Chris@16
|
244 typename fusion::result_of::value_of<First>::type
|
Chris@16
|
245 , typename next_build_acc_list::type>
|
Chris@16
|
246 type;
|
Chris@16
|
247
|
Chris@16
|
248 template<typename Args>
|
Chris@16
|
249 static type
|
Chris@16
|
250 call(Args const &args, First const& f, Last const& l)
|
Chris@16
|
251 {
|
Chris@16
|
252 return type(args, next_build_acc_list::call(args, fusion::next(f), l));
|
Chris@16
|
253 }
|
Chris@16
|
254 };
|
Chris@16
|
255
|
Chris@16
|
256 namespace meta
|
Chris@16
|
257 {
|
Chris@16
|
258 template<typename Sequence>
|
Chris@16
|
259 struct make_acc_list
|
Chris@16
|
260 : build_acc_list<
|
Chris@16
|
261 typename fusion::result_of::begin<Sequence>::type
|
Chris@16
|
262 , typename fusion::result_of::end<Sequence>::type
|
Chris@16
|
263 >
|
Chris@16
|
264 {};
|
Chris@16
|
265 }
|
Chris@16
|
266
|
Chris@16
|
267 template<typename Sequence, typename Args>
|
Chris@16
|
268 typename meta::make_acc_list<Sequence>::type
|
Chris@16
|
269 make_acc_list(Sequence const &seq, Args const &args)
|
Chris@16
|
270 {
|
Chris@16
|
271 return meta::make_acc_list<Sequence>::call(args, fusion::begin(seq), fusion::end(seq));
|
Chris@16
|
272 }
|
Chris@16
|
273
|
Chris@16
|
274 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
275 // checked_as_weighted_feature
|
Chris@16
|
276 template<typename Feature>
|
Chris@16
|
277 struct checked_as_weighted_feature
|
Chris@16
|
278 {
|
Chris@16
|
279 typedef typename as_feature<Feature>::type feature_type;
|
Chris@16
|
280 typedef typename as_weighted_feature<feature_type>::type type;
|
Chris@16
|
281 // weighted and non-weighted flavors should provide the same feature.
|
Chris@16
|
282 BOOST_MPL_ASSERT((
|
Chris@16
|
283 is_same<
|
Chris@16
|
284 typename feature_of<feature_type>::type
|
Chris@16
|
285 , typename feature_of<type>::type
|
Chris@16
|
286 >
|
Chris@16
|
287 ));
|
Chris@16
|
288 };
|
Chris@16
|
289
|
Chris@16
|
290 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
291 // as_feature_list
|
Chris@16
|
292 template<typename Features, typename Weight>
|
Chris@16
|
293 struct as_feature_list
|
Chris@16
|
294 : mpl::transform_view<Features, checked_as_weighted_feature<mpl::_1> >
|
Chris@16
|
295 {
|
Chris@16
|
296 };
|
Chris@16
|
297
|
Chris@16
|
298 template<typename Features>
|
Chris@16
|
299 struct as_feature_list<Features, void>
|
Chris@16
|
300 : mpl::transform_view<Features, as_feature<mpl::_1> >
|
Chris@16
|
301 {
|
Chris@16
|
302 };
|
Chris@16
|
303
|
Chris@16
|
304 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
305 // accumulator_wrapper
|
Chris@16
|
306 template<typename Accumulator, typename Feature>
|
Chris@16
|
307 struct accumulator_wrapper
|
Chris@16
|
308 : Accumulator
|
Chris@16
|
309 {
|
Chris@16
|
310 typedef Feature feature_tag;
|
Chris@16
|
311
|
Chris@16
|
312 accumulator_wrapper(accumulator_wrapper const &that)
|
Chris@16
|
313 : Accumulator(*static_cast<Accumulator const *>(&that))
|
Chris@16
|
314 {
|
Chris@16
|
315 }
|
Chris@16
|
316
|
Chris@16
|
317 template<typename Args>
|
Chris@16
|
318 accumulator_wrapper(Args const &args)
|
Chris@16
|
319 : Accumulator(args)
|
Chris@16
|
320 {
|
Chris@16
|
321 }
|
Chris@16
|
322 };
|
Chris@16
|
323
|
Chris@16
|
324 ///////////////////////////////////////////////////////////////////////////
|
Chris@16
|
325 // to_accumulator
|
Chris@16
|
326 template<typename Feature, typename Sample, typename Weight>
|
Chris@16
|
327 struct to_accumulator
|
Chris@16
|
328 {
|
Chris@16
|
329 typedef
|
Chris@16
|
330 accumulator_wrapper<
|
Chris@16
|
331 typename mpl::apply2<typename Feature::impl, Sample, Weight>::type
|
Chris@16
|
332 , Feature
|
Chris@16
|
333 >
|
Chris@16
|
334 type;
|
Chris@16
|
335 };
|
Chris@16
|
336
|
Chris@16
|
337 template<typename Feature, typename Sample, typename Weight, typename Tag, typename AccumulatorSet>
|
Chris@16
|
338 struct to_accumulator<Feature, Sample, tag::external<Weight, Tag, AccumulatorSet> >
|
Chris@16
|
339 {
|
Chris@16
|
340 BOOST_MPL_ASSERT((is_same<Tag, void>));
|
Chris@16
|
341 BOOST_MPL_ASSERT((is_same<AccumulatorSet, void>));
|
Chris@16
|
342
|
Chris@16
|
343 typedef
|
Chris@16
|
344 accumulator_wrapper<
|
Chris@16
|
345 typename mpl::apply2<typename Feature::impl, Sample, Weight>::type
|
Chris@16
|
346 , Feature
|
Chris@16
|
347 >
|
Chris@16
|
348 accumulator_type;
|
Chris@16
|
349
|
Chris@16
|
350 typedef
|
Chris@16
|
351 typename mpl::if_<
|
Chris@16
|
352 typename Feature::is_weight_accumulator
|
Chris@16
|
353 , accumulator_wrapper<impl::external_impl<accumulator_type, tag::weights>, Feature>
|
Chris@16
|
354 , accumulator_type
|
Chris@16
|
355 >::type
|
Chris@16
|
356 type;
|
Chris@16
|
357 };
|
Chris@16
|
358
|
Chris@16
|
359 // BUGBUG work around an MPL bug wrt map insertion
|
Chris@16
|
360 template<typename FeatureMap, typename Feature>
|
Chris@16
|
361 struct insert_feature
|
Chris@16
|
362 : mpl::eval_if<
|
Chris@16
|
363 mpl::has_key<FeatureMap, typename feature_of<Feature>::type>
|
Chris@16
|
364 , mpl::identity<FeatureMap>
|
Chris@16
|
365 , mpl::insert<FeatureMap, mpl::pair<typename feature_of<Feature>::type, Feature> >
|
Chris@16
|
366 >
|
Chris@16
|
367 {
|
Chris@16
|
368 };
|
Chris@16
|
369
|
Chris@16
|
370 template<typename FeatureMap, typename Feature, typename Weight>
|
Chris@16
|
371 struct insert_dependencies
|
Chris@16
|
372 : mpl::fold<
|
Chris@16
|
373 as_feature_list<typename Feature::dependencies, Weight>
|
Chris@16
|
374 , FeatureMap
|
Chris@16
|
375 , insert_dependencies<
|
Chris@16
|
376 insert_feature<mpl::_1, mpl::_2>
|
Chris@16
|
377 , mpl::_2
|
Chris@16
|
378 , Weight
|
Chris@16
|
379 >
|
Chris@16
|
380 >
|
Chris@16
|
381 {
|
Chris@16
|
382 };
|
Chris@16
|
383
|
Chris@16
|
384 template<typename FeatureMap, typename Features, typename Weight>
|
Chris@16
|
385 struct insert_sequence
|
Chris@16
|
386 : mpl::fold< // BUGBUG should use insert_range, but doesn't seem to work for maps
|
Chris@16
|
387 as_feature_list<Features, Weight>
|
Chris@16
|
388 , FeatureMap
|
Chris@16
|
389 , insert_feature<mpl::_1, mpl::_2>
|
Chris@16
|
390 >
|
Chris@16
|
391 {
|
Chris@16
|
392 };
|
Chris@16
|
393
|
Chris@16
|
394 template<typename Features, typename Sample, typename Weight>
|
Chris@16
|
395 struct make_accumulator_tuple
|
Chris@16
|
396 {
|
Chris@16
|
397 typedef
|
Chris@16
|
398 typename mpl::fold<
|
Chris@16
|
399 as_feature_list<Features, Weight>
|
Chris@16
|
400 , mpl::map0<>
|
Chris@16
|
401 , mpl::if_<
|
Chris@16
|
402 mpl::is_sequence<mpl::_2>
|
Chris@16
|
403 , insert_sequence<mpl::_1, mpl::_2, Weight>
|
Chris@16
|
404 , insert_feature<mpl::_1, mpl::_2>
|
Chris@16
|
405 >
|
Chris@16
|
406 >::type
|
Chris@16
|
407 feature_map;
|
Chris@16
|
408
|
Chris@16
|
409 // for each element in the map, add its dependencies also
|
Chris@16
|
410 typedef
|
Chris@16
|
411 typename mpl::fold<
|
Chris@16
|
412 feature_map
|
Chris@16
|
413 , feature_map
|
Chris@16
|
414 , insert_dependencies<mpl::_1, mpl::second<mpl::_2>, Weight>
|
Chris@16
|
415 >::type
|
Chris@16
|
416 feature_map_with_dependencies;
|
Chris@16
|
417
|
Chris@16
|
418 // turn the map into a vector so we can sort it
|
Chris@16
|
419 typedef
|
Chris@16
|
420 typename mpl::insert_range<
|
Chris@16
|
421 mpl::vector<>
|
Chris@16
|
422 , mpl::end<mpl::vector<> >::type
|
Chris@16
|
423 , mpl::transform_view<feature_map_with_dependencies, mpl::second<mpl::_1> >
|
Chris@16
|
424 >::type
|
Chris@16
|
425 feature_vector_with_dependencies;
|
Chris@16
|
426
|
Chris@16
|
427 // sort the features according to which is derived from which
|
Chris@16
|
428 typedef
|
Chris@16
|
429 typename mpl::sort<
|
Chris@16
|
430 feature_vector_with_dependencies
|
Chris@16
|
431 , is_dependent_on<mpl::_2, mpl::_1>
|
Chris@16
|
432 >::type
|
Chris@16
|
433 sorted_feature_vector;
|
Chris@16
|
434
|
Chris@16
|
435 // From the vector of features, construct a vector of accumulators
|
Chris@16
|
436 typedef
|
Chris@16
|
437 typename mpl::transform<
|
Chris@16
|
438 sorted_feature_vector
|
Chris@16
|
439 , to_accumulator<mpl::_1, Sample, Weight>
|
Chris@16
|
440 >::type
|
Chris@16
|
441 type;
|
Chris@16
|
442 };
|
Chris@16
|
443
|
Chris@16
|
444 } // namespace detail
|
Chris@16
|
445
|
Chris@16
|
446 }} // namespace boost::accumulators
|
Chris@16
|
447
|
Chris@16
|
448 #endif
|