Vcsn  2.1
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 
26  namespace rat
27  {
28 
29  template <typename Context>
32  : ctx_(ctx)
33  , ids_(ids)
34  {
35  require(!ids_.is_distributive() || weightset()->is_commutative(),
36  "series (currently) requires a commutative weightset product");
37  }
38 
39 #define DEFINE \
40  template <typename Context> \
41  inline \
42  auto \
43  expressionset_impl<Context>
44 
46  -> symbol
47  {
48  return symbol("expressionset<" + context_t::sname() + '>');
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  if (fmt == format::latex)
73  {
74  o << "\\mathsf{";
75  switch (identities().ids())
76  {
77  case identities_t::trivial:
78  o << "RatE";
79  break;
80  case identities_t::distributive:
81  o << "Series";
82  break;
83  default:
84  assert(false);
85  };
86  o << "}[";
87  context().print_set(o, fmt);
88  o << ']';
89  }
90  else if (fmt == format::text)
91  {
93  {
94  o << "seriesset<";
95  context().print_set(o, fmt);
96  o << '>';
97  }
98  else
99  {
100  o << "expressionset<";
101  context().print_set(o, fmt);
102  o << '>';
103  if (identities() != vcsn::rat::identities{})
104  o << '(' << identities() << ')';
105  }
106  }
107  else
108  raise("invalid format: ", fmt);
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 (l->type() == type_t::zero)
204  res = r;
205 
206  // E+0 => E.
207  else if (r->type() == type_t::zero)
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 (l->type() == type_t::zero)
367  res = l;
368 
369  // E.0 => 0.
370  else if (r->type() == type_t::zero)
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 (l->type() == type_t::zero)
429  res = l;
430 
431  // E&0 => 0.
432  else if (r->type() == type_t::zero)
433  res = r;
434 
435  // <k>1&<h>1 => <kh>1.
436  else if (type_ignoring_lweight_(l) == type_t::one
437  && type_ignoring_lweight_(r) == type_t::one)
438  res = lmul(weightset()->mul(possibly_implicit_lweight_(l),
439  possibly_implicit_lweight_(r)),
440  one());
441 
442  // <k>a&<h>a => <kh>a. <k>a&<h>b => 0.
443  else if (type_ignoring_lweight_(l) == type_t::atom
444  && type_ignoring_lweight_(r) == type_t::atom)
445  {
446  auto lhs =
447  down_pointer_cast<const atom_t>(unwrap_possible_lweight_(l))->value();
448  auto rhs =
449  down_pointer_cast<const atom_t>(unwrap_possible_lweight_(r))->value();
450  if (labelset()->equal(lhs, rhs))
451  res = rmul(l, possibly_implicit_lweight_(r));
452  else
453  res = zero();
454  }
455 
456  // <k>1&<h>a => 0, <k>a&<h>1 => 0.
457  else if ((type_ignoring_lweight_(l) == type_t::one
458  && type_ignoring_lweight_(r) == type_t::atom)
459  || (type_ignoring_lweight_(l) == type_t::atom
460  && type_ignoring_lweight_(r) == type_t::one))
461  res = zero();
462 
463  else
464  res = std::make_shared<conjunction_t>(gather_<type_t::conjunction>(l, r));
465  return res;
466  }
467 
468  DEFINE::ldiv(const value_t& l, const value_t& r) const
469  -> value_t
470  {
471  value_t res = nullptr;
472 
473  if (!ids_)
474  res = std::make_shared<ldiv_t>(l, r);
475 
476  // 0\E => 0{c}.
477  else if (ids_ && l->type() == type_t::zero)
478  res = complement(zero());
479 
480  // 1\E => E.
481  else if (ids_ && l->type() == type_t::one)
482  res = r;
483 
484  // E\0 => 0.
485  else if (ids_ && r->type() == type_t::zero)
486  res = r;
487 
488  else
489  res = std::make_shared<ldiv_t>(l, r);
490  return res;
491  }
492 
493  DEFINE::rdiv(const value_t& l, const value_t& r) const
494  -> value_t
495  {
496  // l/r = (r{T} {\} l{T}){T}.
497  return transposition(ldiv(transposition(r), transposition(l)));
498  }
499 
500  template <typename Context>
501  template <typename... Value>
502  auto expressionset_impl<Context>::tuple(Value&&... v) const
503  -> value_t
504  {
505  auto ts = as_tupleset();
506  auto t = ts.tuple(v...);
507  if (ts.is_one(t))
508  return one();
509  // If this is a tuple of labels, make it a (multitape) label.
510  // That allows algorithms such as standard, thompson, etc. to work
511  // on lal x lal.
512  //
513  // Note that `\e|a` remains a tuple of expression on lal x lal,
514  // but it is turned into a (multitape) label on lan x lan.
515  else if (tuple_of_label<>::is_label(t))
516  return atom(tuple_of_label<>::as_label(t));
517  else
518  return std::make_shared<tuple_t>(std::forward<Value>(v)...);
519  }
520 
521  DEFINE::infiltration(const value_t& l, const value_t& r) const
522  -> value_t
523  {
524  value_t res = nullptr;
525 
526  if (!ids_)
527  res = std::make_shared<infiltration_t>(l, r);
528 
529  // 0&:E => 0.
530  else if (ids_ && l->type() == type_t::zero)
531  res = l;
532 
533  // E&:0 => 0.
534  else if (ids_ && r->type() == type_t::zero)
535  res = r;
536 
537  // 1&:E => E.
538  else if (ids_ && l->type() == type_t::one)
539  res = r;
540 
541  // E&:1 => E.
542  else if (ids_ && r->type() == type_t::one)
543  res = l;
544 
545  else
546  res =
547  std::make_shared<infiltration_t>(gather_<type_t::infiltration>(l, r));
548  return res;
549  }
550 
551  DEFINE::shuffle(const value_t& l, const value_t& r) const
552  -> value_t
553  {
554  value_t res = nullptr;
555 
556  if (!ids_)
557  res = std::make_shared<shuffle_t>(l, r);
558 
559  // 0:E => 0.
560  else if (ids_ && l->type() == type_t::zero)
561  res = l;
562 
563  // E:0 => 0.
564  else if (ids_ && r->type() == type_t::zero)
565  res = r;
566 
567  // 1:E => E.
568  else if (ids_ && l->type() == type_t::one)
569  res = r;
570 
571  // E:1 => E.
572  else if (ids_ && r->type() == type_t::one)
573  res = l;
574 
575  else
576  res = std::make_shared<shuffle_t>(gather_<type_t::shuffle>(l, r));
577  return res;
578  }
579 
580  /*-------.
581  | power. |
582  `-------*/
583 
584  DEFINE::power(const value_t& e, unsigned n) const
585  -> value_t
586  {
587  value_t res = nullptr;
588  // Given E the expression s.t. E{n} = (<k>a){n}.
589 
590  // E{0} => 1.
591  if (n == 0)
592  res = one();
593 
594  // E{1} => E.
595  else if (n == 1)
596  res = e;
597 
598  // \z{n} => \z.
599  else if (ids_ && e->type() == type_t::zero)
600  res = e;
601 
602  // Case: a == \e or a == <w>\e.
603  else if (ids_ && type_ignoring_lweight_(e) == type_t::one)
604  {
605  weight_t w = possibly_implicit_lweight_(e);
606  res = lmul(weightset()->power(w, n), one());
607  }
608 
609  // Lweight in linear commutative: (<k>E){n} => <k{n}>(E{n}).
610  else if (ids_.is_linear()
611  && weightset()->is_commutative()
612  && e->type() == type_t::lweight)
613  {
614  const auto& lw = down_pointer_cast<const lweight_t>(e);
615  res = lmul(weightset()->power(lw->weight(), n),
616  power(lw->sub(), n));
617  }
618 
619  // Sums in series: we have to distribute ([ab]{2} = aa+ab+ba+bb).
620  else if (ids_.is_distributive() && e->type() == type_t::sum)
621  {
622  // FIXME: code duplication with weightset_mixin::power_.
623  res = e;
624  for (unsigned i = 1; i < n; ++i)
625  res = mul(res, e);
626  }
627 
628  // When associative, instead of repeated multiplication,
629  // immediately create n copies of E.
630  else if (ids_.is_associative())
631  res = std::make_shared<prod_t>(n, e);
632 
633  // Default case: E{n} = ((..(EE)...)E.
634  else
635  {
636  // FIXME: code duplication with weightset_mixin::power_.
637  res = e;
638  for (unsigned i = 1; i < n; ++i)
639  res = mul(res, e);
640  }
641 
642  return res;
643  }
644 
645  DEFINE::concat(const value_t& l, const value_t& r) const
646  -> value_t
647  {
648  // A static dispatch is needed, as the product of labels is not
649  // available if not LAW.
650  return concat_(l, r, typename is_law<Context>::type{});
651  }
652 
653  // Concatenation when not LAW.
654  DEFINE::concat_(const value_t& l, const value_t& r, std::false_type) const
655  -> value_t
656  {
657  return mul(l, r);
658  }
659 
660  // Concatenation when LAW.
661  DEFINE::concat_(const value_t& l, const value_t& r, std::true_type) const
662  -> value_t
663  {
664  // concat((ab).<2>c, d.(ef)) = (ab).<2>(cd).(ef).
665  //
666  // Store (ab) in expression, then concat(<2>c, d) if c and d are
667  // atoms, otherwise <2>c then d, then (ef).
668  if ((type_ignoring_lweight_(l) == type_t::atom || l->type() == type_t::prod)
669  && (r->type() == type_t::atom || r->type() == type_t::prod))
670  {
671  // Left-hand sides.
672  values_t ls;
673  gather_<type_t::prod>(ls, l);
674  // Right-hand sides.
675  values_t rs;
676  gather_<type_t::prod>(rs, r);
677 
678  // FIXME: we should perform that "if" with the one above, and
679  // enter this section only if we really are going to concat.
680  // This would avoid the "else" clause.
681  if (type_ignoring_lweight_(ls.back()) == type_t::atom
682  && rs.front()->type() == type_t::atom)
683  {
684  // Fetch weight and atom of the last lhs.
685  auto w = possibly_implicit_lweight_(ls.back());
686  auto lhs
687  = std::dynamic_pointer_cast<const atom_t>
688  (unwrap_possible_lweight_(ls.back()));
689  // Fetch atom of the first rhs.
690  auto rhs = std::dynamic_pointer_cast<const atom_t>(rs.front());
691  ls.back() = lmul(w,
692  atom(labelset()->mul(lhs->value(), rhs->value())));
693  ls.insert(ls.end(), rs.begin() + 1, rs.end());
694  }
695  else
696  ls.insert(ls.end(), rs.begin(), rs.end());
697  if (ls.size() == 1)
698  return ls.front();
699  else
700  return std::make_shared<prod_t>(std::move(ls));
701  }
702  else
703  // Handle all the trivial identities.
704  return mul(l, r);
705  }
706 
707  DEFINE::star(const value_t& e) const
708  -> value_t
709  {
710  value_t res = nullptr;
711 
712  // \z* => 1.
713  if (ids_ && e->type() == type_t::zero)
714  res = one();
715 
716  else
717  {
718  res = std::make_shared<star_t>(e);
719  require(!ids_.is_distributive() || is_valid(*this, res),
720  "star: not starrable: ", to_string(self(), e));
721  }
722 
723  return res;
724  }
725 
726  DEFINE::complement(const value_t& e) const
727  -> value_t
728  {
729  value_t res = nullptr;
730 
731  if (!ids_)
732  res = std::make_shared<complement_t>(e);
733 
734  // The following identities make derived-term (<2>a)*{c} terminate.
735  // (<k>E){c} => E{c}.
736  else if (auto w = std::dynamic_pointer_cast<const lweight_t>(e))
737  res = complement(w->sub());
738 
739  // (E<k>){c} => E{c}.
740  else if (auto w = std::dynamic_pointer_cast<const rweight_t>(e))
741  res = complement(w->sub());
742 
743  else
744  res = std::make_shared<complement_t>(e);
745 
746  return res;
747  }
748 
749  DEFINE::transposition(const value_t& e) const
750  -> value_t
751  {
752  value_t res = nullptr;
753 
754  if (!ids_)
755  res = std::make_shared<transposition_t>(e);
756 
757  // 0{T} => 0.
758  else if (e->type() == type_t::zero)
759  res = e;
760 
761  // 1{T} => 1.
762  else if (e->type() == type_t::one)
763  res = e;
764 
765  // a{T} => a, (abc){T} => cba.
766  else if (auto l = std::dynamic_pointer_cast<const atom_t>(e))
767  res = atom(labelset()->transpose(l->value()));
768 
769  else
770  res = std::make_shared<transposition_t>(e);
771  return res;
772  }
773 
774  /*----------.
775  | weights. |
776  `----------*/
777 
778  DEFINE::lmul(const weight_t& w, const value_t& e) const
779  -> value_t
780  {
781  value_t res = nullptr;
782 
783  if (!ids_)
784  res = std::make_shared<lweight_t>(w, e);
785 
786  // <k>0 => 0, <1>E => E.
787  else if (e->type() == type_t::zero || weightset()->is_one(w))
788  res = e;
789 
790  // <0>E => 0.
791  else if (weightset()->is_zero(w))
792  res = zero();
793 
794  // <k>(<h>E) => <kh>E.
795  else if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
796  res = lmul(weightset()->mul(w, lw->weight()), lw->sub());
797 
798  // Distributive: <k>(E+F) => <k>E + <k>F.
799  else if (ids_.is_distributive() && e->type() == type_t::sum)
800  {
801  const auto& s = down_pointer_cast<const sum_t>(e);
802  // We can build the result faster by emplace_back'ing addends without
803  // passing thru add; the order will be the same as in *ss.
804  values_t addends;
805  for (const auto& a: *s)
806  addends.emplace_back(lmul(w, a));
807  res = std::make_shared<sum_t>(std::move(addends));
808  }
809 
810  // General case: <k>E.
811  else
812  res = std::make_shared<lweight_t>(w, e);
813 
814  return res;
815  }
816 
817  DEFINE::rmul(const value_t& e, const weight_t& w) const
818  -> value_t
819  {
820  value_t res = nullptr;
821 
822  if (!ids_)
823  res = std::make_shared<rweight_t>(w, e);
824 
825  // Linear identity: E<k> => <k>E.
826  else if (ids_.is_linear() && weightset()->is_commutative())
827  res = lmul(w, e);
828 
829  // Trivial identity: E<0> => 0.
830  else if (weightset()->is_zero(w))
831  res = zero();
832 
833  // Trivial identity: E<1> => E.
834  else if (weightset()->is_one(w))
835  res = e;
836 
837  else if (e->is_leaf())
838  // Can only have left weights and lmul takes care of normalization.
839  res = lmul(w, e);
840 
841  // Trivial identity: (<k>E)<h> => <k>(E<h>).
842  else if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
843  res = lmul(lw->weight(), rmul(lw->sub(), w));
844 
845  // Trivial identity: (E<k>)<h> => E<kh>.
846  else if (auto rw = std::dynamic_pointer_cast<const rweight_t>(e))
847  res = rmul(rw->sub(), weightset()->mul(rw->weight(), w));
848 
849  // General case: E<k>.
850  else
851  res = std::make_shared<rweight_t>(w, e);
852 
853  return res;
854  }
855 
856  /*--------------------------------------.
857  | expressionset as a WeightSet itself. |
858  `--------------------------------------*/
859 
860  DEFINE::is_zero(const value_t& v) const
861  -> bool
862  {
863  return v->type() == type_t::zero;
864  }
865 
866  DEFINE::is_one(const value_t& v)
867  -> bool
868  {
869  return (v->type() == type_t::one);
870  }
871 
873  -> size_t
874  {
875  return rat::size<self_t>(v);
876  }
877 
878  DEFINE::less(const value_t& lhs, const value_t& rhs)
879  -> bool
880  {
881  auto lt = rat::less<self_t>{};
882  return lt(lhs, rhs);
883  }
884 
885  DEFINE::less_linear(const value_t& lhs, const value_t& rhs)
886  -> bool
887  {
888  return less(unwrap_possible_lweight_(lhs),
889  unwrap_possible_lweight_(rhs));
890  }
891 
892  DEFINE::equal(const value_t& lhs, const value_t& rhs)
893  -> bool
894  {
895  return ! less(lhs, rhs) && ! less(rhs, lhs);
896  }
897 
898  DEFINE::hash(const value_t& v)
899  -> size_t
900  {
901  auto hasher = rat::hash<self_t>{};
902  return hasher(v);
903  }
904 
905  DEFINE::conv(const self_t& rs, const value_t& v) const
906  -> value_t
907  {
908  if (ids_ == rs.ids_)
909  return v;
910  else
911  return vcsn::rat::copy(rs, self(), v);
912  }
913 
914  template <typename Context>
915  template <typename GenSet>
916  inline
917  auto
919  typename letterset<GenSet>::value_t v) const
920  -> value_t
921  {
922  return atom(labelset()->conv(ls, v));
923  }
924 
925  DEFINE::conv(b, typename b::value_t v) const
926  -> value_t
927  {
928  return v ? one() : zero();
929  }
930 
931  DEFINE::conv(const z& ws, typename z::value_t v) const
932  -> value_t
933  {
934  return lmul(weightset()->conv(ws, v), one());
935  }
936 
937  DEFINE::conv(const q& ws, typename q::value_t v) const
938  -> value_t
939  {
940  return lmul(weightset()->conv(ws, v), one());
941  }
942 
943  DEFINE::conv(const r& ws, typename r::value_t v) const
944  -> value_t
945  {
946  return lmul(weightset()->conv(ws, v), one());
947  }
948 
949  DEFINE::conv(const zmin& ws, typename zmin::value_t v) const
950  -> value_t
951  {
952  return lmul(weightset()->conv(ws, v), one());
953  }
954 
955  template <typename Context>
956  template <typename Ctx2>
957  inline
958  auto
961  const
962  -> value_t
963  {
964  return vcsn::rat::copy(rs, self(), r);
965  }
966 
967  DEFINE::conv(std::istream& is, bool) const
968  -> value_t
969  {
970  // Our expression parser is written in dyn::, so we get a
971  // dyn::expression that we down_cast.
972  auto dynres
974  is);
975  const auto& res = dynres->template as<self_t>();
976  return res.expression();
977  }
978 
979  DEFINE::print(const value_t& v, std::ostream& o,
980  format fmt) const
981  -> std::ostream&
982  {
983  auto print = make_printer(self(), o);
984  print.format(fmt);
985  return print(v);
986  }
987 
989  -> value_t
990  {
991  auto tr = detail::transposer<self_t>{self()};
992  return tr(v);
993  }
994 
995  template <typename Context>
996  template <typename... Args>
997  inline
998  auto
1000  -> value_t
1001  {
1002  return letter_class_<labelset_t>(std::forward<Args>(args)...,
1003  std::is_same<labelset_t, vcsn::oneset>{});
1004  }
1005 
1006  template <typename Context>
1007  template <typename LabelSet_>
1008  inline
1009  auto
1011  (std::set<std::pair<typename LabelSet_::letter_t,
1012  typename LabelSet_::letter_t>> ccs,
1013  bool accept,
1014  std::false_type) const
1015  -> value_t
1016  {
1017  using boost::range::find;
1018  value_t res = zero();
1019  auto gens = labelset()->genset();
1020  // FIXME: This piece of code screams for factoring. Yet, I want
1021  // to avoid useless costs such as building a empty/full set of
1022  // letters for [^].
1023 
1024  // [a-c].
1025  if (accept)
1026  for (const auto& cc: ccs)
1027  {
1028  auto i = find(gens, cc.first);
1029  auto end = std::find(i, std::end(gens), cc.second);
1030  require(end != std::end(gens),
1031  self(), ": invalid letter interval: ",
1032  to_string(*labelset(), labelset()->value(std::get<0>(cc))),
1033  '-',
1034  to_string(*labelset(), labelset()->value(std::get<1>(cc))));
1035  for (++end; i != end; ++i)
1036  res = add(res, atom(labelset()->value(*i)));
1037  }
1038  // [^].
1039  else if (ccs.empty())
1040  for (auto l: gens)
1041  res = add(res, atom(labelset()->value(l)));
1042  // [^a-z].
1043  else
1044  {
1045  // Match the letters that are in no interval.
1046  std::set<typename LabelSet_::letter_t> accepted;
1047  for (const auto& cc: ccs)
1048  {
1049  auto i = find(gens, cc.first);
1050  auto end = std::find(i, std::end(gens), cc.second);
1051  require(end != std::end(gens),
1052  "invalid letter interval: ",
1053  to_string(*labelset(), labelset()->value(std::get<0>(cc))),
1054  '-',
1055  to_string(*labelset(), labelset()->value(std::get<1>(cc))));
1056  for (++end; i != end; ++i)
1057  accepted.emplace(*i);
1058  }
1059  for (auto c: gens)
1060  if (!has(accepted, c))
1061  res = add(res, atom(labelset()->value(c)));
1062  }
1063  require(!is_zero(res),
1064  "invalid empty letter class");
1065  return res;
1066  }
1067 
1068  template <typename Context>
1069  template <typename LabelSet_, typename... Args>
1070  inline
1071  auto
1073  std::true_type) const
1074  -> value_t
1075  {
1076  return one();
1077  }
1078 
1079 #undef DEFINE
1080 
1081 } // namespace rat
1082 } // namespace vcsn
typename node_t::value_t value_t
An expression (a shared pointer to a tree).
Implementation of labels are letters.
Definition: fwd.hh:10
std::shared_ptr< const detail::context_base > context
A dyn::context.
Definition: fwd.hh:41
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:62
const identities_t ids_
The set of rewriting rules to apply.
std::istringstream is
The input stream: the specification to translate.
Definition: translate.cc:372
std::string to_string(identities i)
Wrapper around operator<<.
Definition: identities.cc:17
bool is_distributive() const
Whether distributive.
Definition: identities.hh:64
char eat(std::istream &is, char c)
Check lookahead character and advance.
Definition: stream.cc:37
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
value_t letter_class(Args &&...chars) const
An expression matching one character amongst chars.
printer< ExpSet > make_printer(const ExpSet &rs, std::ostream &out)
Definition: printer.hh:356
Aut transpose(const transpose_automaton< Aut > &aut)
Definition: transpose.hh:235
return hasher(v)
value_t letter_class_(const Args &&...chars, std::true_type) const
If labelset is oneset.
typename node_t::values_t values_t
A list (vector) of expressions.
Traditional plus distribution. Used for series identities.
Definition: identities.hh:42
A visitor to create a transposed expression,.
Definition: transpose.hh:19
static dyn::context ctx(const driver &d)
Get the context of the driver.
Definition: parse.cc:80
An input/output format.
Definition: format.hh:11
std::ostream & print(const Aut &aut, std::ostream &out, const std::string &format)
Definition: print.hh:77
expressionset_impl(const context_t &ctx, identities_t ids={})
Constructor.
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
A functor to check whether one rational expression is (strictly) less than another one...
Definition: less.hh:21
value_t conv(const letterset< GenSet > &ls, typename letterset< GenSet >::value_t v) const
static identities ids(const driver &d)
Get the identities of the driver.
Definition: parse.cc:87
size_t size(const ExpSet &rs, const typename ExpSet::value_t &r)
const weightset_ptr & weightset() const
Accessor to the weightset.
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:75
context make_context(const std::string &name)
Build a context from its name.
Definition: others.cc:97
auto rs
Definition: lift.hh:151
typename weightset_t::value_t weight_t
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
An expressionset can implement several different sets of identities on expressions.
Definition: identities.hh:21
constant< type_t::zero, Context > zero
Definition: fwd.hh:108
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: pair.hh:243
ATTRIBUTE_PURE bool has(const std::deque< T, Allocator > &s, const T &e)
Whether e is member of s.
Definition: deque.hh:13
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
letter_t value_t
Definition: letterset.hh:32
constant< type_t::one, Context > one
Definition: fwd.hh:111
rat::identities identities(const expression &exp)
Bridge.
Definition: identities.hh:19