Vcsn  2.2a
Be Rational
expressionset.hxx
Go to the documentation of this file.
1 #include <algorithm>
2 #include <cassert>
3 #include <stdexcept>
4 
5 #include <boost/range/algorithm/find.hpp>
6 #include <boost/range/algorithm/lower_bound.hpp>
7 
8 #include <vcsn/algos/fwd.hh> // is-valid
9 #include <vcsn/core/rat/copy.hh>
11 #include <vcsn/core/rat/hash.hh>
12 #include <vcsn/core/rat/less.hh>
13 #include <vcsn/core/rat/size.hh>
15 #include <vcsn/dyn/algos.hh> // dyn::read_expression
16 #include <vcsn/dyn/context.hh> // dyn::make_context
17 #include <vcsn/dyn/fwd.hh>
18 #include <vcsn/labelset/oneset.hh>
19 #include <vcsn/misc/attributes.hh>
20 #include <vcsn/misc/cast.hh>
21 #include <vcsn/misc/stream.hh>
22 
23 namespace vcsn
24 {
25  namespace rat
26  {
27 
28  template <typename Context>
31  : ctx_(ctx)
32  , ids_(ids)
33  {
34  require(!ids_.is_distributive() || weightset()->is_commutative(),
35  "series (currently) requires a commutative weightset product");
36  }
37 
38 #define DEFINE \
39  template <typename Context> \
40  inline \
41  auto \
42  expressionset_impl<Context>
43 
45  -> symbol
46  {
47  static auto res = symbol{"expressionset<" + context_t::sname() + '>'};
48  return res;
49  }
50 
51 
52  DEFINE::make(std::istream& is)
54  {
55  // name is, for instance, "expressionset<lal_char(abcd), z>(trivial)".
56  eat(is, "expressionset<");
57  auto ctx = Context::make(is);
58  eat(is, '>');
59  auto ids = identities_t{};
60  if (is.peek() == '(')
61  {
62  eat(is, '(');
63  is >> ids;
64  eat(is, ')');
65  }
66  return {ctx, ids};
67  }
68 
69  DEFINE::print_set(std::ostream& o, format fmt) const
70  -> std::ostream&
71  {
72  switch (fmt.kind())
73  {
74  case format::latex:
75  o << "\\mathsf{"
76  << (identities().is_distributive() ? "Series" : "RatE")
77  << "}[";
78  context().print_set(o, fmt);
79  o << ']';
80  break;
81  case format::sname:
82  if (identities().is_distributive())
83  {
84  o << "seriesset<";
85  context().print_set(o, fmt);
86  o << '>';
87  }
88  else
89  {
90  o << "expressionset<";
91  context().print_set(o, fmt);
92  o << '>';
93  if (identities() != vcsn::rat::identities{})
94  o << '(' << identities() << ')';
95  }
96  break;
97  case format::text:
98  case format::utf8:
99  o << "RatE[";
100  context().print_set(o, fmt);
101  o << ']';
102  if (identities() != vcsn::rat::identities{})
103  o << '(' << identities() << ')';
104  break;
105  case format::raw:
106  assert(!"print: expression: invalid format: rat");
107  break;
108  }
109  return o;
110  }
111 
112  DEFINE::open(bool o) const
113  -> bool
114  {
115  return this->labelset()->open(o);
116  }
117 
118  DEFINE::context() const -> const context_t&
119  {
120  return ctx_;
121  }
122 
123  DEFINE::identities() const -> identities_t
124  {
125  return ids_;
126  }
127 
128  DEFINE::labelset() const -> const labelset_ptr&
129  {
130  return ctx_.labelset();
131  }
132 
133  DEFINE::weightset() const -> const weightset_ptr&
134  {
135  return ctx_.weightset();
136  }
137 
138  DEFINE::atom(const label_t& v)
139  -> value_t
140  {
141  if (labelset_t::is_one(v))
142  return one();
143  else
144  return std::make_shared<atom_t>(v);
145  }
146 
147  DEFINE::zero() const
148  -> value_t
149  {
150  return std::make_shared<zero_t>();
151  }
152 
153  DEFINE::one()
154  -> value_t
155  {
156  return std::make_shared<one_t>();
157  }
158 
159  template <typename Context>
160  template <typename expressionset_impl<Context>::type_t Type>
161  inline
162  auto
163  expressionset_impl<Context>::gather_(values_t& res, const value_t& v) const
164  -> void
165  {
166  auto variadic = std::dynamic_pointer_cast<const variadic_t<Type>>(v);
167  if (variadic && ids_.is_associative())
168  res.insert(std::end(res), std::begin(*variadic), std::end(*variadic));
169  else
170  res.push_back(v);
171  }
172 
173  template <typename Context>
174  template <typename expressionset_impl<Context>::type_t Type>
175  inline
176  auto
177  expressionset_impl<Context>::gather_(const value_t& l, const value_t& r) const
178  -> values_t
179  {
180  values_t res;
181  if (ids_.is_associative())
182  {
183  gather_<Type>(res, l);
184  gather_<Type>(res, r);
185  }
186  else
187  {
188  res.emplace_back(l);
189  res.emplace_back(r);
190  }
191  return res;
192  }
193 
194  DEFINE::add(const value_t& l, const value_t& r) const
195  -> value_t
196  {
197  value_t res = nullptr;
198 
199  if (!ids_)
200  res = std::make_shared<sum_t>(l, r);
201 
202  // 0+E => E.
203  else if (is_zero(l))
204  res = r;
205 
206  // E+0 => E.
207  else if (is_zero(r))
208  res = l;
209 
210  // E+E => <2>E.
211  else if (ids_.is_linear())
212  res = add_linear_(l, r);
213 
214  else
215  res = std::make_shared<sum_t>(gather_<type_t::sum>(l, r));
216  return res;
217  }
218 
219  DEFINE::add_linear_(const sum_t& s, const value_t& r) const
220  -> value_t
221  {
222  auto res = values_t{};
223  res.reserve(s.size() + 1);
224 
225  // Copy the strictly smaller part.
226  auto i = boost::lower_bound(s, r, less_linear);
227  res.insert(end(res), begin(s), i);
228 
229  if (i == end(s))
230  res.emplace_back(r);
231  else
232  {
233  if (less_linear(r, *i))
234  res.emplace_back(r);
235  else
236  {
237  auto w = weightset()->add(possibly_implicit_lweight_(*i),
238  possibly_implicit_lweight_(r));
239  if (!weightset()->is_zero(w))
240  {
241  auto l = unwrap_possible_lweight_(*i);
242  res.emplace_back(lmul(w, l));
243  }
244  ++i;
245  }
246  res.insert(end(res), i, end(s));
247  }
248 
249  return add_(std::move(res));
250  }
251 
252  DEFINE::add_(values_t&& vs) const
253  -> value_t
254  {
255  if (vs.size() == 0)
256  return zero();
257  else if (vs.size() == 1)
258  return vs[0];
259  else
260  return std::make_shared<sum_t>(std::move(vs));
261  }
262 
263  DEFINE::add_linear_(const sum_t& s1, const sum_t& s2) const
264  -> value_t
265  {
266  auto res = values_t{};
267  res.reserve(s1.size() + s2.size());
268  // Merge two increasing lists. Add weights of equal labels.
269  using std::begin;
270  using std::end;
271  auto i1 = begin(s1), end1 = end(s1);
272  auto i2 = begin(s2), end2 = end(s2);
273  while (true)
274  {
275  if (i1 == end1)
276  {
277  res.insert(res.end(), i2, end2);
278  break;
279  }
280  else if (i2 == end2)
281  {
282  res.insert(res.end(), i1, end1);
283  break;
284  }
285  else if (less_linear(*i1, *i2))
286  res.emplace_back(*i1++);
287  else if (less_linear(*i2, *i1))
288  res.emplace_back(*i2++);
289  else
290  {
291  auto w = weightset()->add(possibly_implicit_lweight_(*i1),
292  possibly_implicit_lweight_(*i2));
293  if (!weightset()->is_zero(w))
294  {
295  auto l = unwrap_possible_lweight_(*i1);
296  res.emplace_back(lmul(w, l));
297  }
298  ++i1;
299  ++i2;
300  }
301  }
302  return add_(std::move(res));
303  }
304 
305  DEFINE::add_linear_(const value_t& l, const value_t& r) const
306  -> value_t
307  {
308  assert(!is_zero(l));
309  assert(!is_zero(r));
310  value_t res = nullptr;
311  if (auto ls = std::dynamic_pointer_cast<const sum_t>(l))
312  {
313  if (auto rs = std::dynamic_pointer_cast<const sum_t>(r))
314  res = add_linear_(*ls, *rs);
315  else
316  res = add_linear_(*ls, r);
317  }
318  else if (auto rs = std::dynamic_pointer_cast<const sum_t>(r))
319  res = add_linear_(*rs, l);
320  else if (less_linear(l, r))
321  res = std::make_shared<sum_t>(l, r);
322  else if (less_linear(r, l))
323  res = std::make_shared<sum_t>(r, l);
324  else
325  {
326  auto w = weightset()->add(possibly_implicit_lweight_(l),
327  possibly_implicit_lweight_(r));
328  res = lmul(w, unwrap_possible_lweight_(l));
329  }
330  return res;
331  }
332 
333  DEFINE::type_ignoring_lweight_(const value_t& e)
334  -> type_t
335  {
336  return unwrap_possible_lweight_(e)->type();
337  }
338 
339  DEFINE::possibly_implicit_lweight_(const value_t& e)
340  -> weight_t
341  {
342  if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
343  return lw->weight();
344  else
345  return weightset_t::one();
346  }
347 
348  DEFINE::unwrap_possible_lweight_(const value_t& e)
349  -> value_t
350  {
351  if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
352  return lw->sub();
353  else
354  return e;
355  }
356 
357  DEFINE::mul(const value_t& l, const value_t& r) const
358  -> value_t
359  {
360  value_t res = nullptr;
361 
362  if (!ids_)
363  res = std::make_shared<prod_t>(l, r);
364 
365  // 0.E => 0.
366  else if (is_zero(l))
367  res = l;
368 
369  // E.0 => 0.
370  else if (is_zero(r))
371  res = r;
372 
373  // U_K: E.(<k>1) ⇒ E<k>, subsuming T: E.1 = E.
374  else if (type_ignoring_lweight_(r) == type_t::one)
375  res = rmul(l, possibly_implicit_lweight_(r));
376 
377  // U_K: (<k>1).E ⇒ <k>E, subsuming T: 1.E = E.
378  else if (type_ignoring_lweight_(l) == type_t::one)
379  res = lmul(possibly_implicit_lweight_(l), r);
380 
381  // (<k>E)(<h>F) => <kh>(EF).
382  else if (ids_.is_linear() && weightset()->is_commutative()
383  && (l->type() == type_t::lweight
384  || r->type() == type_t::lweight))
385  {
386  weight_t
387  lw = possibly_implicit_lweight_(l),
388  rw = possibly_implicit_lweight_(r);
389  value_t
390  nl = unwrap_possible_lweight_(l),
391  nr = unwrap_possible_lweight_(r);
392  res = lmul(weightset()->mul(lw, rw),
393  mul(nl, nr));
394  }
395 
396  // (E+F)G => EG + FG.
397  else if (ids_.is_distributive() && l->type() == type_t::sum)
398  {
399  res = zero();
400  // l is a sum, and r might be as well.
401  for (const auto& la: *down_pointer_cast<const sum_t>(l))
402  res = add(res, mul(la, r));
403  }
404 
405  // E(F+G) => EF + EG.
406  else if (ids_.is_distributive() && r->type() == type_t::sum)
407  {
408  res = zero();
409  // r is a sum, l is not.
410  for (const auto& ra: *down_pointer_cast<const sum_t>(r))
411  res = add(res, mul(l, ra));
412  }
413 
414  else
415  res = std::make_shared<prod_t>(gather_<type_t::prod>(l, r));
416  return res;
417  }
418 
419  DEFINE::conjunction(const value_t& l, const value_t& r) const
420  -> value_t
421  {
422  value_t res = nullptr;
423 
424  if (!ids_)
425  res = std::make_shared<conjunction_t>(l, r);
426 
427  // 0&E => 0.
428  else if (is_zero(l))
429  res = l;
430 
431  // E&0 => 0.
432  else if (is_zero(r))
433  res = r;
434 
435  // E&0{c} => E.
436  else if (is_universal(r))
437  res = l;
438 
439  // 0{c}&E => E.
440  else if (is_universal(l))
441  res = r;
442 
443  // <k>1&<h>1 => <kh>1.
444  else if (type_ignoring_lweight_(l) == type_t::one
445  && type_ignoring_lweight_(r) == type_t::one)
446  res = lmul(weightset()->mul(possibly_implicit_lweight_(l),
447  possibly_implicit_lweight_(r)),
448  one());
449 
450  // <k>a&<h>a => <kh>a. <k>a&<h>b => 0.
451  else if (type_ignoring_lweight_(l) == type_t::atom
452  && type_ignoring_lweight_(r) == type_t::atom)
453  {
454  auto lhs =
455  down_pointer_cast<const atom_t>(unwrap_possible_lweight_(l))->value();
456  auto rhs =
457  down_pointer_cast<const atom_t>(unwrap_possible_lweight_(r))->value();
458  if (labelset()->equal(lhs, rhs))
459  res = rmul(l, possibly_implicit_lweight_(r));
460  else
461  res = zero();
462  }
463 
464  // <k>1&<h>a => 0, <k>a&<h>1 => 0.
465  else if ((type_ignoring_lweight_(l) == type_t::one
466  && type_ignoring_lweight_(r) == type_t::atom)
467  || (type_ignoring_lweight_(l) == type_t::atom
468  && type_ignoring_lweight_(r) == type_t::one))
469  res = zero();
470 
471  else
472  res = std::make_shared<conjunction_t>(gather_<type_t::conjunction>(l, r));
473  return res;
474  }
475 
476  DEFINE::ldiv(const value_t& l, const value_t& r) const
477  -> value_t
478  {
479  value_t res = nullptr;
480 
481  if (!ids_)
482  res = std::make_shared<ldiv_t>(l, r);
483 
484  // 0\E => 0{c}.
485  else if (ids_ && is_zero(l))
486  res = complement(zero());
487 
488  // 1\E => E.
489  else if (ids_ && is_one(l))
490  res = r;
491 
492  // E\0 => 0.
493  else if (ids_ && is_zero(r))
494  res = r;
495 
496  else
497  res = std::make_shared<ldiv_t>(l, r);
498  return res;
499  }
500 
501  DEFINE::rdiv(const value_t& l, const value_t& r) const
502  -> value_t
503  {
504  // l/r = (r{T} {\} l{T}){T}.
505  return transposition(ldiv(transposition(r), transposition(l)));
506  }
507 
508  template <typename Context>
509  template <typename... Value>
510  auto expressionset_impl<Context>::tuple(Value&&... v) const
511  -> value_t
512  {
513  auto ts = as_tupleset();
514  auto t = ts.tuple(v...);
515  if (ts.is_one(t))
516  return one();
517  // If this is a tuple of labels, make it a (multitape) label.
518  // That allows algorithms such as standard, thompson, etc. to work
519  // on lal x lal.
520  //
521  // Note that `\e|a` remains a tuple of expression on lal x lal,
522  // but it is turned into a (multitape) label on lan x lan.
523  else if (tuple_of_label<>::is_label(t))
524  return atom(tuple_of_label<>::as_label(t));
525  else
526  return std::make_shared<tuple_t>(std::forward<Value>(v)...);
527  }
528 
529  DEFINE::infiltration(const value_t& l, const value_t& r) const
530  -> value_t
531  {
532  value_t res = nullptr;
533 
534  if (!ids_)
535  res = std::make_shared<infiltration_t>(l, r);
536 
537  // 0&:E => 0.
538  else if (ids_ && is_zero(l))
539  res = l;
540 
541  // E&:0 => 0.
542  else if (ids_ && is_zero(r))
543  res = r;
544 
545  // 1&:E => E.
546  else if (ids_ && is_one(l))
547  res = r;
548 
549  // E&:1 => E.
550  else if (ids_ && is_one(r))
551  res = l;
552 
553  else
554  res =
555  std::make_shared<infiltration_t>(gather_<type_t::infiltration>(l, r));
556  return res;
557  }
558 
559  DEFINE::shuffle(const value_t& l, const value_t& r) const
560  -> value_t
561  {
562  value_t res = nullptr;
563 
564  if (!ids_)
565  res = std::make_shared<shuffle_t>(l, r);
566 
567  // 0:E => 0.
568  else if (ids_ && is_zero(l))
569  res = l;
570 
571  // E:0 => 0.
572  else if (ids_ && is_zero(r))
573  res = r;
574 
575  // 1:E => E.
576  else if (ids_ && is_one(l))
577  res = r;
578 
579  // E:1 => E.
580  else if (ids_ && is_one(r))
581  res = l;
582 
583  else
584  res = std::make_shared<shuffle_t>(gather_<type_t::shuffle>(l, r));
585  return res;
586  }
587 
588  /*-------.
589  | power. |
590  `-------*/
591 
592  DEFINE::power(const value_t& e, unsigned n) const
593  -> value_t
594  {
595  value_t res = nullptr;
596  // Given E the expression s.t. E{n} = (<k>a){n}.
597 
598  // E{0} => 1.
599  if (n == 0)
600  res = one();
601 
602  // E{1} => E.
603  else if (n == 1)
604  res = e;
605 
606  // \z{n} => \z.
607  else if (ids_ && is_zero(e))
608  res = e;
609 
610  // Case: a == \e or a == <w>\e.
611  else if (ids_ && type_ignoring_lweight_(e) == type_t::one)
612  {
613  weight_t w = possibly_implicit_lweight_(e);
614  res = lmul(weightset()->power(w, n), one());
615  }
616 
617  // Lweight in linear commutative: (<k>E){n} => <k{n}>(E{n}).
618  else if (ids_.is_linear()
619  && weightset()->is_commutative()
620  && e->type() == type_t::lweight)
621  {
622  const auto& lw = down_pointer_cast<const lweight_t>(e);
623  res = lmul(weightset()->power(lw->weight(), n),
624  power(lw->sub(), n));
625  }
626 
627  // Sums in series: we have to distribute ([ab]{2} = aa+ab+ba+bb).
628  else if (ids_.is_distributive() && e->type() == type_t::sum)
629  {
630  // FIXME: code duplication with weightset_mixin::power_.
631  res = e;
632  for (unsigned i = 1; i < n; ++i)
633  res = mul(res, e);
634  }
635 
636  // When associative, instead of repeated multiplication,
637  // immediately create n copies of E.
638  else if (ids_.is_associative())
639  res = std::make_shared<prod_t>(n, e);
640 
641  // Default case: E{n} = ((..(EE)...)E.
642  else
643  {
644  // FIXME: code duplication with weightset_mixin::power_.
645  res = e;
646  for (unsigned i = 1; i < n; ++i)
647  res = mul(res, e);
648  }
649 
650  return res;
651  }
652 
653  DEFINE::concat(const value_t& l, const value_t& r) const
654  -> value_t
655  {
656  // A static dispatch is needed, as the product of labels is not
657  // available if not LAW.
658  return concat_(l, r, typename is_law<Context>::type{});
659  }
660 
661  // Concatenation when not LAW.
662  DEFINE::concat_(const value_t& l, const value_t& r, std::false_type) const
663  -> value_t
664  {
665  return mul(l, r);
666  }
667 
668  // Concatenation when LAW.
669  DEFINE::concat_(const value_t& l, const value_t& r, std::true_type) const
670  -> value_t
671  {
672  // concat((ab).<2>c, d.(ef)) = (ab).<2>(cd).(ef).
673  //
674  // Store (ab) in expression, then concat(<2>c, d) if c and d are
675  // atoms, otherwise <2>c then d, then (ef).
676  if ((type_ignoring_lweight_(l) == type_t::atom || l->type() == type_t::prod)
677  && (r->type() == type_t::atom || r->type() == type_t::prod))
678  {
679  // Left-hand sides.
680  values_t ls;
681  gather_<type_t::prod>(ls, l);
682  // Right-hand sides.
683  values_t rs;
684  gather_<type_t::prod>(rs, r);
685 
686  // FIXME: we should perform that "if" with the one above, and
687  // enter this section only if we really are going to concat.
688  // This would avoid the "else" clause.
689  if (type_ignoring_lweight_(ls.back()) == type_t::atom
690  && rs.front()->type() == type_t::atom)
691  {
692  // Fetch weight and atom of the last lhs.
693  auto w = possibly_implicit_lweight_(ls.back());
694  auto lhs
695  = std::dynamic_pointer_cast<const atom_t>
696  (unwrap_possible_lweight_(ls.back()));
697  // Fetch atom of the first rhs.
698  auto rhs = std::dynamic_pointer_cast<const atom_t>(rs.front());
699  ls.back() = lmul(w,
700  atom(labelset()->mul(lhs->value(), rhs->value())));
701  ls.insert(ls.end(), rs.begin() + 1, rs.end());
702  }
703  else
704  ls.insert(ls.end(), rs.begin(), rs.end());
705  if (ls.size() == 1)
706  return ls.front();
707  else
708  return std::make_shared<prod_t>(std::move(ls));
709  }
710  else
711  // Handle all the trivial identities.
712  return mul(l, r);
713  }
714 
715  DEFINE::star(const value_t& e) const
716  -> value_t
717  {
718  value_t res = nullptr;
719 
720  // \z* => 1.
721  if (ids_ && is_zero(e))
722  res = one();
723 
724  else
725  {
726  res = std::make_shared<star_t>(e);
727  VCSN_REQUIRE(!ids_.is_distributive() || is_valid(*this, res),
728  "star: not starrable: ", to_string(self(), e));
729  }
730 
731  return res;
732  }
733 
734  DEFINE::complement(const value_t& e) const
735  -> value_t
736  {
737  value_t res = nullptr;
738 
739  if (!ids_)
740  res = std::make_shared<complement_t>(e);
741 
742  // The following identities make derived-term (<2>a)*{c} terminate.
743  // (<k>E){c} => E{c}.
744  else if (auto w = std::dynamic_pointer_cast<const lweight_t>(e))
745  res = complement(w->sub());
746 
747  // (E<k>){c} => E{c}.
748  else if (auto w = std::dynamic_pointer_cast<const rweight_t>(e))
749  res = complement(w->sub());
750 
751  // E{c}{c} => E if on B or F2.
752  //
753  // Indeed, (<2>a)*{c}{c} is actually denoting a*, not (<2>a)*.
754  else if (e->type() == type_t::complement
755  && std::is_same<weight_t, bool>{})
756  res = down_pointer_cast<const complement_t>(e)->sub();
757 
758  else
759  res = std::make_shared<complement_t>(e);
760 
761  return res;
762  }
763 
764  DEFINE::transposition(const value_t& e) const
765  -> value_t
766  {
767  value_t res = nullptr;
768 
769  if (!ids_)
770  res = std::make_shared<transposition_t>(e);
771 
772  // 0{T} => 0.
773  else if (is_zero(e))
774  res = e;
775 
776  // 1{T} => 1.
777  else if (is_one(e))
778  res = e;
779 
780  // a{T} => a, (abc){T} => cba.
781  else if (auto l = std::dynamic_pointer_cast<const atom_t>(e))
782  res = atom(labelset()->transpose(l->value()));
783 
784  else
785  res = std::make_shared<transposition_t>(e);
786  return res;
787  }
788 
789  /*----------.
790  | weights. |
791  `----------*/
792 
793  DEFINE::lmul(const weight_t& w, const value_t& e) const
794  -> value_t
795  {
796  value_t res = nullptr;
797 
798  if (!ids_)
799  res = std::make_shared<lweight_t>(w, e);
800 
801  // <k>0 => 0, <1>E => E.
802  else if (is_zero(e) || weightset()->is_one(w))
803  res = e;
804 
805  // <0>E => 0.
806  else if (weightset()->is_zero(w))
807  res = zero();
808 
809  // <k>(<h>E) => <kh>E.
810  else if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
811  res = lmul(weightset()->mul(w, lw->weight()), lw->sub());
812 
813  // Distributive: <k>(E+F) => <k>E + <k>F.
814  else if (ids_.is_distributive() && e->type() == type_t::sum)
815  {
816  const auto& s = down_pointer_cast<const sum_t>(e);
817  // We can build the result faster by emplace_back'ing addends without
818  // passing thru add; the order will be the same as in *ss.
819  values_t addends;
820  for (const auto& a: *s)
821  addends.emplace_back(lmul(w, a));
822  res = std::make_shared<sum_t>(std::move(addends));
823  }
824 
825  // General case: <k>E.
826  else
827  res = std::make_shared<lweight_t>(w, e);
828 
829  return res;
830  }
831 
832  DEFINE::rmul(const value_t& e, const weight_t& w) const
833  -> value_t
834  {
835  value_t res = nullptr;
836 
837  if (!ids_)
838  res = std::make_shared<rweight_t>(w, e);
839 
840  // Linear identity: E<k> => <k>E.
841  else if (ids_.is_linear() && weightset()->is_commutative())
842  res = lmul(w, e);
843 
844  // Trivial identity: E<0> => 0.
845  else if (weightset()->is_zero(w))
846  res = zero();
847 
848  // Trivial identity: E<1> => E.
849  else if (weightset()->is_one(w))
850  res = e;
851 
852  else if (e->is_leaf())
853  // Can only have left weights and lmul takes care of normalization.
854  res = lmul(w, e);
855 
856  // Trivial identity: (<k>E)<h> => <k>(E<h>).
857  else if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
858  res = lmul(lw->weight(), rmul(lw->sub(), w));
859 
860  // Trivial identity: (E<k>)<h> => E<kh>.
861  else if (auto rw = std::dynamic_pointer_cast<const rweight_t>(e))
862  res = rmul(rw->sub(), weightset()->mul(rw->weight(), w));
863 
864  // General case: E<k>.
865  else
866  res = std::make_shared<rweight_t>(w, e);
867 
868  return res;
869  }
870 
871  /*--------------------------------------.
872  | expressionset as a WeightSet itself. |
873  `--------------------------------------*/
874 
875  DEFINE::is_zero(const value_t& v) const
876  -> bool
877  {
878  return v->type() == type_t::zero;
879  }
880 
881  DEFINE::is_one(const value_t& v)
882  -> bool
883  {
884  return v->type() == type_t::one;
885  }
886 
887  DEFINE::is_universal(const value_t& v) const
888  -> bool
889  {
890  return (v->type() == type_t::complement
891  && is_zero(down_pointer_cast<const complement_t>(v)->sub()));
892  }
893 
895  -> size_t
896  {
897  return rat::size<self_t>(v);
898  }
899 
900  DEFINE::less(const value_t& lhs, const value_t& rhs)
901  -> bool
902  {
903  auto lt = rat::less<self_t>{};
904  return lt(lhs, rhs);
905  }
906 
907  DEFINE::less_linear(const value_t& lhs, const value_t& rhs)
908  -> bool
909  {
910  return less(unwrap_possible_lweight_(lhs),
911  unwrap_possible_lweight_(rhs));
912  }
913 
914  DEFINE::equal(const value_t& lhs, const value_t& rhs)
915  -> bool
916  {
917  return ! less(lhs, rhs) && ! less(rhs, lhs);
918  }
919 
920  DEFINE::hash(const value_t& v)
921  -> size_t
922  {
923  auto hasher = rat::hash<self_t>{};
924  return hasher(v);
925  }
926 
927  DEFINE::conv(const self_t& rs, const value_t& v) const
928  -> value_t
929  {
930  if (ids_ == rs.ids_)
931  return v;
932  else
933  return vcsn::rat::copy(rs, self(), v);
934  }
935 
936  template <typename Context>
937  template <typename GenSet>
938  inline
939  auto
941  typename letterset<GenSet>::value_t v) const
942  -> value_t
943  {
944  return atom(labelset()->conv(ls, v));
945  }
946 
947  DEFINE::conv(b, typename b::value_t v) const
948  -> value_t
949  {
950  return v ? one() : zero();
951  }
952 
953  DEFINE::conv(const z& ws, typename z::value_t v) const
954  -> value_t
955  {
956  return lmul(weightset()->conv(ws, v), one());
957  }
958 
959  DEFINE::conv(const q& ws, typename q::value_t v) const
960  -> value_t
961  {
962  return lmul(weightset()->conv(ws, v), one());
963  }
964 
965  DEFINE::conv(const r& ws, typename r::value_t v) const
966  -> value_t
967  {
968  return lmul(weightset()->conv(ws, v), one());
969  }
970 
971  DEFINE::conv(const zmin& ws, typename zmin::value_t v) const
972  -> value_t
973  {
974  return lmul(weightset()->conv(ws, v), one());
975  }
976 
977  template <typename Context>
978  template <typename Ctx2>
979  inline
980  auto
983  const
984  -> value_t
985  {
986  return vcsn::rat::copy(rs, self(), r);
987  }
988 
989  DEFINE::conv(std::istream& is, bool) const
990  -> value_t
991  {
992  // Our expression parser is written in dyn::, so we get a
993  // dyn::expression that we down_cast.
994  auto dynres
996  is);
997  const auto& res = dynres->template as<self_t>();
998  return res.expression();
999  }
1000 
1001  DEFINE::print(const value_t& v, std::ostream& o,
1002  format fmt) const
1003  -> std::ostream&
1004  {
1005  auto print = make_printer(self(), o);
1006  print.format(fmt);
1007  return print(v);
1008  }
1009 
1011  -> value_t
1012  {
1013  auto tr = detail::transposer<self_t>{self()};
1014  return tr(v);
1015  }
1016 
1017  template <typename Context>
1018  template <typename... Args>
1019  inline
1020  auto
1022  -> value_t
1023  {
1024  return letter_class_<labelset_t>(std::forward<Args>(args)...,
1025  std::is_same<labelset_t, vcsn::oneset>{});
1026  }
1027 
1028  template <typename Context>
1029  template <typename LabelSet_>
1030  inline
1031  auto
1033  (std::set<std::pair<typename LabelSet_::letter_t,
1034  typename LabelSet_::letter_t>> ccs,
1035  bool accept,
1036  std::false_type) const
1037  -> value_t
1038  {
1039  using boost::range::find;
1040  value_t res = zero();
1041  const auto& ls = *labelset();
1042  auto gens = ls.generators();
1043  // FIXME: This piece of code screams for factoring. Yet, I want
1044  // to avoid useless costs such as building a empty/full set of
1045  // letters for [^].
1046 
1047  // [a-c].
1048  if (accept)
1049  for (const auto& cc: ccs)
1050  {
1051  auto i = find(gens, cc.first);
1052  auto end = std::find(i, std::end(gens), cc.second);
1053  VCSN_REQUIRE(end != std::end(gens),
1054  self(), ": invalid letter interval: ",
1055  to_string(*labelset(), ls.value(std::get<0>(cc))),
1056  '-',
1057  to_string(*labelset(), ls.value(std::get<1>(cc))));
1058  for (++end; i != end; ++i)
1059  res = add(res, atom(ls.value(*i)));
1060  }
1061  // [^].
1062  else if (ccs.empty())
1063  for (auto l: gens)
1064  res = add(res, atom(ls.value(l)));
1065  // [^a-z].
1066  else
1067  {
1068  // Match the letters that are in no interval.
1069  std::set<typename LabelSet_::letter_t> accepted;
1070  for (const auto& cc: ccs)
1071  {
1072  auto i = find(gens, cc.first);
1073  auto end = std::find(i, std::end(gens), cc.second);
1074  VCSN_REQUIRE(end != std::end(gens),
1075  "invalid letter interval: ",
1076  to_string(*labelset(), ls.value(std::get<0>(cc))),
1077  '-',
1078  to_string(*labelset(), ls.value(std::get<1>(cc))));
1079  for (++end; i != end; ++i)
1080  accepted.emplace(*i);
1081  }
1082  for (auto c: gens)
1083  if (!has(accepted, c))
1084  res = add(res, atom(ls.value(c)));
1085  }
1086  require(!is_zero(res),
1087  "invalid empty letter class");
1088  return res;
1089  }
1090 
1091  template <typename Context>
1092  template <typename LabelSet_, typename... Args>
1093  inline
1094  auto
1096  std::true_type) const
1097  -> value_t
1098  {
1099  return one();
1100  }
1101 
1102 #undef DEFINE
1103 
1104 } // namespace rat
1105 } // namespace vcsn
std::istringstream is
The input stream: the specification to translate.
Definition: translate.cc:380
printer< ExpSet > make_printer(const ExpSet &rs, std::ostream &out)
Definition: printer.hh:377
constant< type_t::one, Context > one
Definition: fwd.hh:111
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
symbol sname()
Definition: name.hh:67
boost::flyweight< std::string, boost::flyweights::no_tracking, boost::flyweights::intermodule_holder > symbol
An internalized string.
Definition: symbol.hh:23
typename node_t::value_t value_t
An expression (a shared pointer to a tree).
rat::identities identities(const expression &exp)
Bridge.
Definition: identities.hh:19
return hasher(v)
auto conv(const letterset< GenSet > &ls, typename letterset< GenSet >::value_t v) const -> value_t
std::ostream & print(const Aut &aut, std::ostream &out, const std::string &fmt)
Definition: print.hh:78
expression read_expression(const context &ctx, rat::identities ids, std::istream &is, const std::string &format="default")
Read an expression from a stream.
Definition: read.cc:59
ATTRIBUTE_PURE bool has(const boost::container::flat_set< Key, Compare, Allocator > &s, const Key &e)
Whether e is member of s.
Definition: setalpha.hh:25
typename weightset_t::value_t weight_t
auto conv(const ValueSet &vs, const std::string &str, Args &&...args) -> decltype(vs.conv(std::declval< std::istream & >(), std::forward< Args >(args)...))
Parse str via vs.conv.
Definition: stream.hh:28
Print as a parsable type string.
Definition: format.hh:24
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: pair.hh:251
std::shared_ptr< const detail::context_base > context
A dyn::context.
Definition: fwd.hh:26
auto rs
Definition: lift.hh:151
Implementation of labels are letters.
Definition: fwd.hh:11
Print for LaTeX.
Definition: format.hh:20
#define VCSN_REQUIRE(Cond,...)
A macro similar to require.
Definition: raise.hh:89
auto letter_class_(const Args &&...chars, std::true_type) const -> value_t
If labelset is oneset.
typename node_t::values_t values_t
A list (vector) of expressions.
A functor to check whether one rational expression is (strictly) less than another one...
Definition: less.hh:21
void require(Bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:78
expressionset_impl(const context_t &ctx, identities_t ids={})
Constructor.
Request the set implementation (bool weights).
letter_t value_t
Definition: letterset.hh:32
std::string to_string(identities i)
Wrapper around operator<<.
Definition: identities.cc:17
context make_context(const std::string &name)
Build a context from its name.
Definition: others.cc:99
OutExpSet::value_t copy(const InExpSet &in_rs, const OutExpSet &out_rs, const typename InExpSet::value_t &v)
Copy/convert a rational expression.
Definition: copy.hh:183
char eat(std::istream &is, char c)
Check lookahead character and advance.
Definition: stream.cc:37
const weightset_ptr & weightset() const
Accessor to the weightset.
static identities ids(const driver &d)
Get the identities of the driver.
Definition: parse.cc:89
const identities_t ids_
The set of rewriting rules to apply.
static dyn::context ctx(const driver &d)
Get the context of the driver.
Definition: parse.cc:82
size_t size(const ExpSet &rs, const typename ExpSet::value_t &r)
auto letter_class(Args &&...chars) const -> value_t
An expression matching one character amongst chars.
A visitor to create a transposed expression,.
Definition: transpose.hh:19
bool is_distributive() const
Whether distributive.
Definition: identities.hh:64
Aut transpose(const transpose_automaton< Aut > &aut)
Definition: transpose.hh:227
An expressionset can implement several different sets of identities on expressions.
Definition: identities.hh:21
An input/output format for valuesets.
Definition: format.hh:11
constant< type_t::zero, Context > zero
Definition: fwd.hh:108
Definition: a-star.hh:8