calendar.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // Range v3 library
  2. //
  3. // Copyright Eric Niebler 2013-present
  4. //
  5. // Use, modification and distribution is subject to the
  6. // Boost Software License, Version 1.0. (See accompanying
  7. // file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. // Project home: https://github.com/ericniebler/range-v3
  11. //
  12. #include <range/v3/detail/config.hpp>
  13. #if RANGES_CXX_RETURN_TYPE_DEDUCTION >= RANGES_CXX_RETURN_TYPE_DEDUCTION_14 && \
  14. RANGES_CXX_GENERIC_LAMBDAS >= RANGES_CXX_GENERIC_LAMBDAS_14
  15. ///[calendar]
  16. // Usage:
  17. // calendar 2015
  18. //
  19. // Output:
  20. /*
  21. January February March
  22. 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 6 7
  23. 4 5 6 7 8 9 10 8 9 10 11 12 13 14 8 9 10 11 12 13 14
  24. 11 12 13 14 15 16 17 15 16 17 18 19 20 21 15 16 17 18 19 20 21
  25. 18 19 20 21 22 23 24 22 23 24 25 26 27 28 22 23 24 25 26 27 28
  26. 25 26 27 28 29 30 31 29 30 31
  27. April May June
  28. 1 2 3 4 1 2 1 2 3 4 5 6
  29. 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13
  30. 12 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20
  31. 19 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27
  32. 26 27 28 29 30 24 25 26 27 28 29 30 28 29 30
  33. 31
  34. July August September
  35. 1 2 3 4 1 1 2 3 4 5
  36. 5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12
  37. 12 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19
  38. 19 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26
  39. 26 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30
  40. 30 31
  41. October November December
  42. 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5
  43. 4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12
  44. 11 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19
  45. 18 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26
  46. 25 26 27 28 29 30 31 29 30 27 28 29 30 31
  47. // */
  48. // Credits:
  49. // Thanks to H. S. Teoh for the article that served as the
  50. // inspiration for this example:
  51. // <http://wiki.dlang.org/Component_programming_with_ranges>
  52. // Thanks to github's Arzar for bringing date::week_number
  53. // to my attention.
  54. #include <boost/date_time/gregorian/gregorian.hpp>
  55. #include <boost/format.hpp>
  56. #include <boost/lexical_cast.hpp>
  57. #include <boost/program_options.hpp>
  58. #include <algorithm>
  59. #include <cstddef>
  60. #include <functional>
  61. #include <iostream>
  62. #include <range/v3/action/join.hpp>
  63. #include <range/v3/algorithm/copy.hpp>
  64. #include <range/v3/algorithm/for_each.hpp>
  65. #include <range/v3/algorithm/mismatch.hpp>
  66. #include <range/v3/core.hpp>
  67. #include <range/v3/iterator/stream_iterators.hpp>
  68. #include <range/v3/view/all.hpp>
  69. #include <range/v3/view/chunk.hpp>
  70. #include <range/v3/view/chunk_by.hpp>
  71. #include <range/v3/view/concat.hpp>
  72. #include <range/v3/view/iota.hpp>
  73. #include <range/v3/view/join.hpp>
  74. #include <range/v3/view/repeat_n.hpp>
  75. #include <range/v3/view/single.hpp>
  76. #include <range/v3/view/take.hpp>
  77. #include <range/v3/view/transform.hpp>
  78. #include <stdexcept>
  79. #include <string>
  80. #include <utility>
  81. #include <vector>
  82. namespace po = boost::program_options;
  83. namespace greg = boost::gregorian;
  84. using date = greg::date;
  85. using day = greg::date_duration;
  86. using namespace ranges;
  87. namespace boost
  88. {
  89. namespace gregorian
  90. {
  91. date &operator++(date &d)
  92. {
  93. return d = d + day(1);
  94. }
  95. date operator++(date &d, int)
  96. {
  97. return ++d - day(1);
  98. }
  99. }
  100. }
  101. namespace ranges
  102. {
  103. template<>
  104. struct incrementable_traits<date>
  105. {
  106. using difference_type = date::duration_type::duration_rep::int_type;
  107. };
  108. }
  109. CPP_assert(incrementable<date>);
  110. auto
  111. dates(unsigned short start, unsigned short stop)
  112. {
  113. return views::iota(date{start, greg::Jan, 1}, date{stop, greg::Jan, 1});
  114. }
  115. auto
  116. dates_from(unsigned short year)
  117. {
  118. return views::iota(date{year, greg::Jan, 1});
  119. }
  120. auto
  121. by_month()
  122. {
  123. return views::chunk_by(
  124. [](date a, date b) { return a.month() == b.month(); });
  125. }
  126. auto
  127. by_week()
  128. {
  129. return views::chunk_by([](date a, date b) {
  130. // ++a because week_number is Mon-Sun and we want Sun-Sat
  131. return (++a).week_number() == (++b).week_number();
  132. });
  133. }
  134. std::string
  135. format_day(date d)
  136. {
  137. return boost::str(boost::format("%|3|") % d.day());
  138. }
  139. // In: range<range<date>>: month grouped by weeks.
  140. // Out: range<std::string>: month with formatted weeks.
  141. auto
  142. format_weeks()
  143. {
  144. return views::transform([](/*range<date>*/ auto week) {
  145. return boost::str(boost::format("%1%%2%%|22t|") %
  146. std::string(front(week).day_of_week() * 3u, ' ') %
  147. (week | views::transform(format_day) | actions::join));
  148. });
  149. }
  150. // Return a formatted string with the title of the month
  151. // corresponding to a date.
  152. std::string
  153. month_title(date d)
  154. {
  155. return boost::str(boost::format("%|=22|") % d.month().as_long_string());
  156. }
  157. // In: range<range<date>>: year of months of days
  158. // Out: range<range<std::string>>: year of months of formatted wks
  159. auto
  160. layout_months()
  161. {
  162. return views::transform([](/*range<date>*/ auto month) {
  163. auto week_count =
  164. static_cast<std::ptrdiff_t>(distance(month | by_week()));
  165. return views::concat(
  166. views::single(month_title(front(month))),
  167. month | by_week() | format_weeks(),
  168. views::repeat_n(std::string(22, ' '), 6 - week_count));
  169. });
  170. }
  171. // Flattens a range of ranges by iterating the inner
  172. // ranges in round-robin fashion.
  173. template<class Rngs>
  174. class interleave_view : public view_facade<interleave_view<Rngs>>
  175. {
  176. friend range_access;
  177. std::vector<range_value_t<Rngs>> rngs_;
  178. struct cursor;
  179. cursor begin_cursor()
  180. {
  181. return {0, &rngs_, views::transform(rngs_, ranges::begin) | to<std::vector>};
  182. }
  183. public:
  184. interleave_view() = default;
  185. explicit interleave_view(Rngs rngs)
  186. : rngs_(std::move(rngs) | to<std::vector>)
  187. {}
  188. };
  189. template<class Rngs>
  190. struct interleave_view<Rngs>::cursor
  191. {
  192. std::size_t n_;
  193. std::vector<range_value_t<Rngs>> *rngs_;
  194. std::vector<iterator_t<range_value_t<Rngs>>> its_;
  195. decltype(auto) read() const
  196. {
  197. return *its_[n_];
  198. }
  199. void next()
  200. {
  201. if(0 == ((++n_) %= its_.size()))
  202. for_each(its_, [](auto &it) { ++it; });
  203. }
  204. bool equal(default_sentinel_t) const
  205. {
  206. if(n_ != 0)
  207. return false;
  208. auto ends = *rngs_ | views::transform(ranges::end);
  209. return its_.end() != std::mismatch(
  210. its_.begin(), its_.end(), ends.begin(), std::not_equal_to<>{}).first;
  211. }
  212. CPP_member
  213. auto equal(cursor const& that) const -> CPP_ret(bool)(
  214. requires forward_range<range_value_t<Rngs>>)
  215. {
  216. return n_ == that.n_ && its_ == that.its_;
  217. }
  218. };
  219. // In: range<range<T>>
  220. // Out: range<T>, flattened by walking the ranges
  221. // round-robin fashion.
  222. auto
  223. interleave()
  224. {
  225. return make_view_closure([](auto &&rngs) {
  226. using Rngs = decltype(rngs);
  227. return interleave_view<views::all_t<Rngs>>(
  228. views::all(std::forward<Rngs>(rngs)));
  229. });
  230. }
  231. // In: range<range<T>>
  232. // Out: range<range<T>>, transposing the rows and columns.
  233. auto
  234. transpose()
  235. {
  236. return make_view_closure([](auto &&rngs) {
  237. using Rngs = decltype(rngs);
  238. CPP_assert(forward_range<Rngs>);
  239. return std::forward<Rngs>(rngs)
  240. | interleave()
  241. | views::chunk(static_cast<std::size_t>(distance(rngs)));
  242. });
  243. }
  244. // In: range<range<range<string>>>
  245. // Out: range<range<range<string>>>, transposing months.
  246. auto
  247. transpose_months()
  248. {
  249. return views::transform(
  250. [](/*range<range<string>>*/ auto rng) { return rng | transpose(); });
  251. }
  252. // In: range<range<string>>
  253. // Out: range<string>, joining the strings of the inner ranges
  254. auto
  255. join_months()
  256. {
  257. return views::transform(
  258. [](/*range<string>*/ auto rng) { return actions::join(rng); });
  259. }
  260. // In: range<date>
  261. // Out: range<string>, lines of formatted output
  262. auto
  263. format_calendar(std::size_t months_per_line)
  264. {
  265. return
  266. // Group the dates by month:
  267. by_month()
  268. // Format the month into a range of strings:
  269. | layout_months()
  270. // Group the months that belong side-by-side:
  271. | views::chunk(months_per_line)
  272. // Transpose the rows and columns of the size-by-side months:
  273. | transpose_months()
  274. // Ungroup the side-by-side months:
  275. | views::join
  276. // Join the strings of the transposed months:
  277. | join_months();
  278. }
  279. int
  280. main(int argc, char *argv[]) try
  281. {
  282. // Declare the supported options.
  283. po::options_description desc("Allowed options");
  284. desc.add_options()("help", "produce help message")(
  285. "start", po::value<unsigned short>(), "Year to start")(
  286. "stop", po::value<std::string>(), "Year to stop")(
  287. "per-line",
  288. po::value<std::size_t>()->default_value(3u),
  289. "Nbr of months per line");
  290. po::positional_options_description p;
  291. p.add("start", 1).add("stop", 1);
  292. po::variables_map vm;
  293. po::store(
  294. po::command_line_parser(argc, argv).options(desc).positional(p).run(),
  295. vm);
  296. po::notify(vm);
  297. if(vm.count("help") || 1 != vm.count("start"))
  298. {
  299. std::cerr << desc << '\n';
  300. return 1;
  301. }
  302. auto const start = vm["start"].as<unsigned short>();
  303. auto const stop = 0 == vm.count("stop")
  304. ? (unsigned short)(start + 1)
  305. : vm["stop"].as<std::string>() == "never"
  306. ? (unsigned short)-1
  307. : boost::lexical_cast<unsigned short>(
  308. vm["stop"].as<std::string>());
  309. auto const months_per_line = vm["per-line"].as<std::size_t>();
  310. if(stop != (unsigned short)-1 && stop <= start)
  311. {
  312. std::cerr << "ERROR: The stop year must be larger than the start"
  313. << '\n';
  314. return 1;
  315. }
  316. if((unsigned short)-1 != stop)
  317. {
  318. copy(dates(start, stop) | format_calendar(months_per_line),
  319. ostream_iterator<>(std::cout, "\n"));
  320. }
  321. else
  322. {
  323. copy(dates_from(start) | format_calendar(months_per_line),
  324. ostream_iterator<>(std::cout, "\n"));
  325. }
  326. }
  327. catch(std::exception &e)
  328. {
  329. std::cerr << "ERROR: Unhandled exception\n";
  330. std::cerr << " what(): " << e.what();
  331. return 1;
  332. }
  333. ///[calendar]
  334. #else
  335. #pragma message( \
  336. "calendar requires C++14 return type deduction and generic lambdas")
  337. int
  338. main()
  339. {}
  340. #endif