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