Vcsn  2.8
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
10 #include <vcsn/core/rat/copy.hh>
12 #include <vcsn/core/rat/hash.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/algorithm.hh>
20 #include <vcsn/misc/attributes.hh>
21 #include <vcsn/misc/cast.hh>
22 #include <vcsn/misc/stream.hh>
23 #include <vcsn/misc/tuple.hh>
24 
25 namespace vcsn
26 {
27  namespace rat
28  {
29 
30  template <typename Context>
33  : ctx_(ctx)
34  , ids_(ids)
35  {
36  require(!ids_.is_distributive() || weightset()->is_commutative(),
37  "series (currently) requires a commutative weightset product");
38  }
39 
40 #define DEFINE \
41  template <typename Context> \
42  auto \
43  expressionset_impl<Context>
44 
46  -> symbol
47  {
48  static auto res = symbol{"expressionset<" + context_t::sname() + '>'};
49  return res;
50  }
51 
52 
53  DEFINE::make(std::istream& is)
55  {
56  // name is, for instance, "expressionset<lal_char(abcd), z>(trivial)".
57  eat(is, "expressionset<");
58  auto ctx = Context::make(is);
59  eat(is, '>');
60  auto ids = identities_t{};
61  if (is.peek() == '(')
62  {
63  eat(is, '(');
64  is >> ids;
65  eat(is, ')');
66  }
67  return {ctx, ids};
68  }
69 
70  DEFINE::print_set(std::ostream& o, format fmt) const
71  -> std::ostream&
72  {
73  switch (fmt.kind())
74  {
75  case format::latex:
76  o << "\\mathsf{"
77  << (identities().is_distributive() ? "Series" : "RatE")
78  << "}[";
79  context().print_set(o, fmt);
80  o << ']';
81  break;
82  case format::sname:
83  if (identities().is_distributive())
84  {
85  o << "seriesset<";
86  context().print_set(o, fmt);
87  o << '>';
88  }
89  else
90  {
91  o << "expressionset<";
92  context().print_set(o, fmt);
93  o << '>';
94  if (identities() != vcsn::rat::identities{})
95  o << '(' << identities() << ')';
96  }
97  break;
98  case format::text:
99  case format::utf8:
100  o << "RatE[";
101  context().print_set(o, fmt);
102  o << ']';
103  if (identities() != vcsn::rat::identities{})
104  o << '(' << identities() << ')';
105  break;
106  case format::raw:
107  assert(!"expressionset::print_set: invalid format: rat");
108  break;
109  }
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::name(const value_t& v, symbol name) const
149  -> value_t
150  {
151  return std::make_shared<name_t>(v, name);
152  }
153 
154  DEFINE::zero()
155  -> value_t
156  {
157  return std::make_shared<zero_t>();
158  }
159 
160  DEFINE::one()
161  -> value_t
162  {
163  return std::make_shared<one_t>();
164  }
165 
166  template <typename Context>
167  template <typename expressionset_impl<Context>::type_t Type>
168  auto
169  expressionset_impl<Context>::gather_(values_t& res, const value_t& v) const
170  -> void
171  {
172  auto variadic = std::dynamic_pointer_cast<const variadic_t<Type>>(v);
173  if (variadic && ids_.is_associative())
174  res.insert(std::end(res), std::begin(*variadic), std::end(*variadic));
175  else
176  res.push_back(v);
177  }
178 
179  template <typename Context>
180  template <typename expressionset_impl<Context>::type_t Type>
181  auto
182  expressionset_impl<Context>::gather_(const value_t& l, const value_t& r) const
183  -> values_t
184  {
185  auto res = values_t{};
186  if (ids_.is_associative())
187  {
188  gather_<Type>(res, l);
189  gather_<Type>(res, r);
190  }
191  else
192  {
193  res.emplace_back(l);
194  res.emplace_back(r);
195  }
196  return res;
197  }
198 
199  DEFINE::add(const value_t& l, const value_t& r) const
200  -> value_t
201  {
202  auto res = value_t{};
203 
204  // 0+E => E.
205  if (ids_.is_trivial() && is_zero(l))
206  res = r;
207 
208  // E+0 => E.
209  else if (ids_.is_trivial() && is_zero(r))
210  res = l;
211 
212  // E+E => <2>E.
213  else if (ids_.is_linear())
214  res = add_linear_(l, r);
215 
216  else
217  res = std::make_shared<add_t>(gather_<type_t::add>(l, r));
218  return res;
219  }
220 
221  DEFINE::add_linear_(const add_t& s, const value_t& r) const
222  -> value_t
223  {
224  auto res = values_t{};
225  res.reserve(s.size() + 1);
226 
227  // Copy the strictly smaller part.
228  auto i = boost::lower_bound(s, r, less_linear);
229  res.insert(end(res), begin(s), i);
230 
231  if (i == end(s))
232  res.emplace_back(r);
233  else
234  {
235  if (less_linear(r, *i))
236  res.emplace_back(r);
237  else
238  {
239  auto w = weightset()->add(possibly_implicit_lweight_(*i),
240  possibly_implicit_lweight_(r));
241  if (!weightset()->is_zero(w))
242  {
243  auto l = unwrap_possible_lweight_(*i);
244  res.emplace_back(lweight(w, l));
245  }
246  ++i;
247  }
248  res.insert(end(res), i, end(s));
249  }
250 
251  return add_(std::move(res));
252  }
253 
254  DEFINE::add_(values_t&& vs) const
255  -> value_t
256  {
257  if (vs.size() == 0)
258  return zero();
259  else if (vs.size() == 1)
260  return vs[0];
261  else
262  return std::make_shared<add_t>(std::move(vs));
263  }
264 
265  DEFINE::add_linear_(const add_t& s1, const add_t& s2) const
266  -> value_t
267  {
268  auto res = values_t{};
269  res.reserve(s1.size() + s2.size());
270  // Merge two increasing lists. Add weights of equal labels.
271  using std::begin;
272  using std::end;
273  auto i1 = begin(s1), end1 = end(s1);
274  auto i2 = begin(s2), end2 = end(s2);
275  while (true)
276  {
277  if (i1 == end1)
278  {
279  res.insert(res.end(), i2, end2);
280  break;
281  }
282  else if (i2 == end2)
283  {
284  res.insert(res.end(), i1, end1);
285  break;
286  }
287  else if (less_linear(*i1, *i2))
288  res.emplace_back(*i1++);
289  else if (less_linear(*i2, *i1))
290  res.emplace_back(*i2++);
291  else
292  {
293  auto w = weightset()->add(possibly_implicit_lweight_(*i1),
294  possibly_implicit_lweight_(*i2));
295  if (!weightset()->is_zero(w))
296  {
297  auto l = unwrap_possible_lweight_(*i1);
298  res.emplace_back(lweight(w, l));
299  }
300  ++i1;
301  ++i2;
302  }
303  }
304  return add_(std::move(res));
305  }
306 
307  DEFINE::add_linear_(const value_t& l, const value_t& r) const
308  -> value_t
309  {
310  assert(!is_zero(l));
311  assert(!is_zero(r));
312  auto res = value_t{};
313  if (auto ls = std::dynamic_pointer_cast<const add_t>(l))
314  {
315  if (auto rs = std::dynamic_pointer_cast<const add_t>(r))
316  res = add_linear_(*ls, *rs);
317  else
318  res = add_linear_(*ls, r);
319  }
320  else if (auto rs = std::dynamic_pointer_cast<const add_t>(r))
321  res = add_linear_(*rs, l);
322  else if (less_linear(l, r))
323  res = std::make_shared<add_t>(l, r);
324  else if (less_linear(r, l))
325  res = std::make_shared<add_t>(r, l);
326  else
327  {
328  auto w = weightset()->add(possibly_implicit_lweight_(l),
329  possibly_implicit_lweight_(r));
330  res = lweight(w, unwrap_possible_lweight_(l));
331  }
332  return res;
333  }
334 
335  DEFINE::type_ignoring_lweight_(const value_t& e)
336  -> type_t
337  {
338  return unwrap_possible_lweight_(e)->type();
339  }
340 
341  DEFINE::possibly_implicit_lweight_(const value_t& e)
342  -> weight_t
343  {
344  if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
345  return lw->weight();
346  else
347  return weightset_t::one();
348  }
349 
350  DEFINE::unwrap_possible_lweight_(const value_t& e)
351  -> value_t
352  {
353  if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
354  return lw->sub();
355  else
356  return e;
357  }
358 
359  DEFINE::mul(const value_t& l, const value_t& r) const
360  -> value_t
361  {
362  auto res = value_t{};
363 
364  // 0.E => 0.
365  if (ids_.is_trivial() && is_zero(l))
366  res = l;
367 
368  // E.0 => 0.
369  else if (ids_.is_trivial() && is_zero(r))
370  res = r;
371 
372  // U_K: E.(<k>1) ⇒ E<k>, subsuming T: E.1 = E.
373  else if (ids_.is_trivial() && type_ignoring_lweight_(r) == type_t::one)
374  res = rweight(l, possibly_implicit_lweight_(r));
375 
376  // U_K: (<k>1).E ⇒ <k>E, subsuming T: 1.E = E.
377  else if (ids_.is_trivial() && type_ignoring_lweight_(l) == type_t::one)
378  res = lweight(possibly_implicit_lweight_(l), r);
379 
380  // (<k>E)(<h>F) => <kh>(EF).
381  else if (ids_.is_linear() && weightset()->is_commutative()
382  && (l->type() == type_t::lweight
383  || r->type() == type_t::lweight))
384  {
385  // FIXME: check that it can't shadow the following items.
386  weight_t
389  value_t
390  nl = unwrap_possible_lweight_(l),
392  res = lweight(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::add)
398  {
399  res = zero();
400  // l is a sum, and r might be as well.
401  for (const auto& la: *down_pointer_cast<const add_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::add)
407  {
408  res = zero();
409  // r is a sum, l is not.
410  for (const auto& ra: *down_pointer_cast<const add_t>(r))
411  res = add(res, mul(l, ra));
412  }
413 
414  else
415  res = std::make_shared<mul_t>(gather_<type_t::mul>(l, r));
416  return res;
417  }
418 
419  DEFINE::compose(const value_t& l, const value_t& r) const
420  -> value_t
421  {
422  auto res = value_t{};
423 
424  // 0 @ E => 0.
425  if (ids_.is_trivial() && is_zero(l))
426  res = l;
427 
428  // E @ 0 => 0.
429  else if (ids_.is_trivial() && is_zero(r))
430  res = r;
431 
432  // <k>1 @ <h>1 => <kh>1
433  else if (ids_.is_trivial()
438  one());
439 
440  // a|\e @ 1 => a|\e, 1@\e|a => \e|a, a|x@x|b => a|x, a|x@y|b => 0.
441  else if (ids_.is_linear()
443  && context_t::has_one()
449  // 1 behaves exactly like `\e|\e`.
450  //
451  // So we want the `out` label of l, and the `in` label of r.
452  // FIXME: this piece of code does not belong to here, it should
453  // be handled in the realm of labelsets.
454  detail::static_if<are_composable<context_t>()
455  && context_t::has_one()
457  ([this, &res](const auto& ls, const auto& l, const auto& r)
458  {
459  // Tape of the lhs on which we compose. Cannot use just
460  // `ls.size()`, clang++ 3.9 and 4.0 do not accept it, which
461  // seems like a bug to me.
462  constexpr auto out
464  // Tape of the rhs on which we compose.
465  constexpr auto in = 0;
466  // The common (single-tape) labelset.
467  const auto& ls0 = ls.template set<out>();
468  // FIXME: maybe_compose, or dynamic are_composable.
469  auto get_label = [&](const auto& e, auto tape)
470  {
471  if (e->type() == type_t::atom)
472  return std::get<decltype(tape){}>
473  (down_pointer_cast<const atom_t>(e)->value());
474  else
475  return ls0.one();
476  };
477  auto lhs = unwrap_possible_lweight_(l);
478  auto rhs = unwrap_possible_lweight_(r);
479  auto lout = get_label(l, std::integral_constant<int, out>{});
480  auto lin = get_label(r, std::integral_constant<int, in>{});
481  // 1 @ x|b => x|b if x == \e, 0 otherwise.
482  if (lhs->type() == type_t::one && rhs->type() == type_t::atom)
483  res = ls0.is_one(lin) ? rhs : zero();
484  // a|x @ 1 => a|x if x == \e, 0 otherwise.
485  else if (lhs->type() == type_t::atom && rhs->type() == type_t::one)
486  res = ls0.is_one(lout) ? lhs : zero();
487  // a|x @ y|b => a|b if x == y, 0 otherwise.
488  else
489  res = (ls0.equal(lout, lin)
490  ? atom(ls.compose
491  (ls,
492  down_pointer_cast<const atom_t>(lhs)->value(),
493  ls,
494  down_pointer_cast<const atom_t>(rhs)->value()))
495  : zero());
498  res);
499  },
500  [](const auto&, const auto&, const auto&)
501  {
503  })
504  (*labelset(), l, r);
505 
506  // General case.
507  else
508  res = std::make_shared<compose_t>(gather_<type_t::compose>(l, r));
509  return res;
510  }
511 
512  DEFINE::conjunction(const value_t& l, const value_t& r) const
513  -> value_t
514  {
515  auto res = value_t{};
516 
517  // 0&E => 0.
518  if (ids_.is_trivial() && is_zero(l))
519  res = l;
520 
521  // E&0 => 0.
522  else if (ids_.is_trivial() && is_zero(r))
523  res = r;
524 
525  // E&0{c} => E.
526  else if (ids_.is_trivial() && is_universal(r))
527  res = l;
528 
529  // 0{c}&E => E.
530  else if (ids_.is_trivial() && is_universal(l))
531  res = r;
532 
533  // <k>1&<h>1 => <kh>1.
534  else if (ids_.is_trivial()
539  one());
540 
541  // <k>a&<h>a => <kh>a. <k>a&<h>b => 0.
542  else if (ids_.is_trivial()
545  {
546  auto lhs =
548  auto rhs =
549  down_pointer_cast<const atom_t>(unwrap_possible_lweight_(r))->value();
550  if (labelset()->equal(lhs, rhs))
552  else
553  res = zero();
554  }
555 
556  // <k>1&<h>a => 0, <k>a&<h>1 => 0.
557  else if (ids_.is_trivial()
562  res = zero();
563 
564  // General case: E & F.
565  else
566  res = std::make_shared<conjunction_t>(gather_<type_t::conjunction>(l, r));
567  return res;
568  }
569 
570  DEFINE::ldivide(const value_t& l, const value_t& r) const
571  -> value_t
572  {
573  auto res = value_t{};
574 
575  // 0\E => 0.
576  if (ids_.is_trivial() && is_zero(l))
577  res = l;
578 
579  // 1\E => E.
580  else if (ids_.is_trivial() && is_one(l))
581  res = r;
582 
583  // E\0 => 0.
584  else if (ids_.is_trivial() && is_zero(r))
585  res = r;
586 
587  else
588  res = std::make_shared<ldivide_t>(l, r);
589  return res;
590  }
591 
592  DEFINE::rdivide(const value_t& l, const value_t& r) const
593  -> value_t
594  {
595  // l/r = (r{T} {\} l{T}){T}.
597  }
598 
599  template <typename Context>
600  template <typename... Value>
601  auto expressionset_impl<Context>::tuple(Value&&... v) const
602  -> value_t
603  {
604  auto ts = as_tupleset();
605  auto t = ts.tuple(v...);
606 
607  // \e | \e => \e.
608  //
609  // This is critical, it is not a identity, it is more important
610  // than that. For instance, the tupling of expansions starts by
611  // denormalizing them and then normalizes the result. So
612  // `<2>|<3>` becomes `\e.[<2>\e] | \e.[<3>\e]` which becomes
613  // `\e|\e.[<6>(\e|\e]` But if we don't recognize that `\e|\e` is
614  // really `\e`, the normalization fails to turn it into `<6>`.
615  if (ts.is_one(t))
616  return one();
617 
618  // \z | E => \z.
619  //
620  // FIXME: maybe we should introduce a short-circuiting version
621  // that would not make useless invocation when a \z was found.
622  else if (ids_.is_trivial()
623  && detail::apply(any{},
624  detail::apply(([](const auto& rs, const auto& r)
625  { return rs.is_zero(r); }),
626  ts.sets(), t)))
627  return zero();
628 
629  // If this is a tuple of labels, make it a (multitape) label.
630  // That allows algorithms such as standard, thompson, etc. to work
631  // on lal x lal.
632  //
633  // Note that `\e|a` remains a tuple of expressions on lal x lal,
634  // but it is turned into a (multitape) label on lan x lal.
635  else if (ids_.is_trivial() && tuple_of_label<>{self()}.is_label(t))
636  return atom(tuple_of_label<>{self()}.as_label(t));
637 
638  // General case.
639  else
640  return std::make_shared<tuple_t>(std::forward<Value>(v)...);
641  }
642 
643  DEFINE::infiltrate(const value_t& l, const value_t& r) const
644  -> value_t
645  {
646  auto res = value_t{};
647 
648  // 0 &: E => 0.
649  if (ids_.is_trivial() && is_zero(l))
650  res = l;
651 
652  // E &: 0 => 0.
653  else if (ids_.is_trivial() && is_zero(r))
654  res = r;
655 
656  // 1 &: E => E.
657  else if (ids_.is_trivial() && is_one(l))
658  res = r;
659 
660  // E &: 1 => E.
661  else if (ids_.is_trivial() && is_one(r))
662  res = l;
663 
664  else
665  res =
666  std::make_shared<infiltrate_t>(gather_<type_t::infiltrate>(l, r));
667  return res;
668  }
669 
670  DEFINE::shuffle(const value_t& l, const value_t& r) const
671  -> value_t
672  {
673  auto res = value_t{};
674 
675  // 0:E => 0.
676  if (ids_.is_trivial() && is_zero(l))
677  res = l;
678 
679  // E:0 => 0.
680  else if (ids_.is_trivial() && is_zero(r))
681  res = r;
682 
683  // 1:E => E.
684  else if (ids_.is_trivial() && is_one(l))
685  res = r;
686 
687  // E:1 => E.
688  else if (ids_.is_trivial() && is_one(r))
689  res = l;
690 
691  else
692  res = std::make_shared<shuffle_t>(gather_<type_t::shuffle>(l, r));
693  return res;
694  }
695 
696  /*-------.
697  | power. |
698  `-------*/
699 
700  DEFINE::power(const value_t& e, unsigned n) const
701  -> value_t
702  {
703  auto res = value_t{};
704  // Given E the expression s.t. E{n} = (<k>a){n}.
705 
706  // E{0} => 1.
707  if (n == 0)
708  res = one();
709 
710  // E{1} => E.
711  else if (n == 1)
712  res = e;
713 
714  // \z{n} => \z.
715  else if (ids_.is_trivial() && is_zero(e))
716  res = e;
717 
718  // Case: a == \e or a == <w>\e.
719  else if (ids_.is_trivial()
721  {
723  res = lweight(weightset()->power(w, n), one());
724  }
725 
726  // Lweight in linear commutative: (<k>E){n} => <k{n}>(E{n}).
727  else if (ids_.is_linear()
728  && weightset()->is_commutative()
729  && e->type() == type_t::lweight)
730  {
731  const auto& lw = down_pointer_cast<const lweight_t>(e);
732  res = lweight(weightset()->power(lw->weight(), n),
733  power(lw->sub(), n));
734  }
735 
736  // Sums in series: we have to distribute ([ab]{2} = aa+ab+ba+bb).
737  else if (ids_.is_distributive() && e->type() == type_t::add)
738  {
739  // FIXME: code duplication with weightset_mixin::power_.
740  res = e;
741  for (unsigned i = 1; i < n; ++i)
742  res = mul(res, e);
743  }
744 
745  // When associative, instead of repeated multiplication,
746  // immediately create n copies of E.
747  else if (ids_.is_associative())
748  res = std::make_shared<mul_t>(n, e);
749 
750  // Default case: E{n} = ((..(EE)...)E.
751  else
752  {
753  // FIXME: code duplication with weightset_mixin::power_.
754  res = e;
755  for (unsigned i = 1; i < n; ++i)
756  res = mul(res, e);
757  }
758 
759  return res;
760  }
761 
762  DEFINE::concat(const value_t& l, const value_t& r) const
763  -> value_t
764  {
765  // A static dispatch is needed, as the product of labels is not
766  // available if not LAW.
767  return concat_(l, r, typename is_law<Context>::type{});
768  }
769 
770  // Concatenation when not LAW.
771  DEFINE::concat_(const value_t& l, const value_t& r, std::false_type) const
772  -> value_t
773  {
774  return mul(l, r);
775  }
776 
777  // Concatenation when LAW.
778  DEFINE::concat_(const value_t& l, const value_t& r, std::true_type) const
779  -> value_t
780  {
781  // For instance:
782  // concat((ab).<2>c, d.(ef)) = (ab).<2>(cd).(ef).
783  //
784  // Store (ab) in expression, then concat(<2>c, d) if c and d are
785  // atoms, otherwise <2>c then d, then (ef).
786  if ((type_ignoring_lweight_(l) == type_t::atom || l->type() == type_t::mul)
787  && (r->type() == type_t::atom || r->type() == type_t::mul))
788  {
789  // Left-hand sides.
790  auto ls = values_t{};
791  gather_<type_t::mul>(ls, l);
792  // Right-hand sides.
793  auto rs = values_t{};
794  gather_<type_t::mul>(rs, r);
795 
796  // FIXME: we should perform that "if" with the one above, and
797  // enter this section only if we really are going to concat.
798  // This would avoid the "else" clause.
799  if (type_ignoring_lweight_(ls.back()) == type_t::atom
800  && rs.front()->type() == type_t::atom)
801  {
802  // Fetch atom of the last lhs.
803  auto lhs = down_pointer_cast<const atom_t>
804  (unwrap_possible_lweight_(ls.back()));
805  // Fetch atom of the first rhs.
806  auto rhs = down_pointer_cast<const atom_t>(rs.front());
807 
808  auto product = atom(labelset()->mul(lhs->value(), rhs->value()));
809 
810  if (ls.back()->type() == type_t::lweight)
811  {
812  const auto& lw = down_pointer_cast<const lweight_t>(ls.back());
813  product = lweight(lw->weight(), product);
814  }
815  ls.back() = product;
816 
817  ls.insert(ls.end(), rs.begin() + 1, rs.end());
818  }
819  else
820  ls.insert(ls.end(), rs.begin(), rs.end());
821  if (ls.size() == 1)
822  return ls.front();
823  else
824  return std::make_shared<mul_t>(std::move(ls));
825  }
826  else
827  // Handle all the trivial identities.
828  return mul(l, r);
829  }
830 
831  DEFINE::star(const value_t& e) const
832  -> value_t
833  {
834  auto res = value_t{};
835 
836  // \z* => 1.
837  if (ids_.is_trivial() && is_zero(e))
838  res = one();
839 
840  // E** => E* if on B.
841  else if (ids_.is_agressive()
842  && e->type() == type_t::star
843  && std::is_same<weightset_t, b>{})
844  res = e;
845 
846  else
847  {
848  res = std::make_shared<star_t>(e);
849  if (ids_.is_distributive() && !is_valid(*this, res))
850  raise_not_starrable(self(), e);
851  }
852 
853  return res;
854  }
855 
856  DEFINE::complement(const value_t& e) const
857  -> value_t
858  {
859  auto res = value_t{};
860 
861  // The following identities make derived-term (<2>a)*{c} terminate.
862  // (<k>E){c} => E{c}.
863  if (ids_.is_trivial() && e->type() == type_t::lweight)
864  {
865  auto w = down_pointer_cast<const lweight_t>(e);
866  res = complement(w->sub());
867  }
868 
869  // (E<k>){c} => E{c}.
870  else if (ids_.is_trivial() && e->type() == type_t::rweight)
871  {
872  auto w = down_pointer_cast<const rweight_t>(e);
873  res = complement(w->sub());
874  }
875 
876  // E{c}{c} => E if on B or F2.
877  //
878  // Indeed, (<2>a)*{c}{c} is actually denoting a*, not (<2>a)*.
879  else if (ids_.is_trivial()
880  && e->type() == type_t::complement
881  && std::is_same<weight_t, bool>{})
882  res = down_pointer_cast<const complement_t>(e)->sub();
883 
884  else
885  res = std::make_shared<complement_t>(e);
886 
887  return res;
888  }
889 
891  -> value_t
892  {
893  auto res = value_t{};
894 
895  // E{T} => E{t} when agressive.
896  if (ids_.is_agressive())
897  res = transpose(e);
898 
899  // 0{T} => 0.
900  else if (ids_.is_trivial() && is_zero(e))
901  res = e;
902 
903  // 1{T} => 1.
904  else if (ids_.is_trivial() && is_one(e))
905  res = e;
906 
907  // a{T} => a, (abc){T} => cba.
908  else if (ids_.is_trivial() && e->type() == type_t::atom)
909  {
910  auto l = down_pointer_cast<const atom_t>(e);
911  res = atom(labelset()->transpose(l->value()));
912  }
913 
914  // E{T}{T} => E, if agressive.
915  else if (ids_.is_agressive()
916  && e->type() == type_t::transposition)
917  res = down_pointer_cast<const transposition_t>(e)->sub();
918 
919  else
920  res = std::make_shared<transposition_t>(e);
921  return res;
922  }
923 
924  /*----------.
925  | weights. |
926  `----------*/
927 
928  DEFINE::lweight(const weight_t& w, const value_t& e) const
929  -> value_t
930  {
931  auto res = value_t{};
932 
933  // <k>0 => 0, <1>E => E.
934  if (ids_.is_trivial()
935  && (is_zero(e) || weightset()->is_one(w)))
936  res = e;
937 
938  // <0>E => 0.
939  else if (ids_.is_trivial() && weightset()->is_zero(w))
940  res = zero();
941 
942  // <k>(<h>E) => <kh>E.
943  else if (ids_.is_trivial() && e->type() == type_t::lweight)
944  {
945  auto lw = down_pointer_cast<const lweight_t>(e);
946  res = lweight(weightset()->mul(w, lw->weight()), lw->sub());
947  }
948 
949  // Distributive: <k>(E+F) => <k>E + <k>F.
950  else if (ids_.is_distributive() && e->type() == type_t::add)
951  {
952  const auto& s = down_pointer_cast<const add_t>(e);
953  // We can build the result faster by emplace_back'ing addends without
954  // passing thru add; the order will be the same as in *ss.
955  auto addends = values_t{};
956  for (const auto& a: *s)
957  addends.emplace_back(lweight(w, a));
958  res = std::make_shared<add_t>(std::move(addends));
959  }
960 
961  // General case: <k>E.
962  else
963  res = std::make_shared<lweight_t>(w, e);
964 
965  return res;
966  }
967 
968  DEFINE::rweight(const value_t& e, const weight_t& w) const
969  -> value_t
970  {
971  auto res = value_t{};
972 
973  // Linear identity: E<k> => <k>E.
974  if (ids_.is_linear() && weightset()->is_commutative())
975  res = lweight(w, e);
976 
977  // Trivial identity: E<0> => 0.
978  else if (ids_.is_trivial() && weightset()->is_zero(w))
979  res = zero();
980 
981  // Trivial identity: E<1> => E.
982  else if (ids_.is_trivial() && weightset()->is_one(w))
983  res = e;
984 
985  else if (ids_.is_trivial() && e->is_leaf())
986  // Can only have left weights and lweight takes care of normalization.
987  res = lweight(w, e);
988 
989  // Trivial identity: (<k>E)<h> => <k>(E<h>).
990  else if (ids_.is_trivial() && e->type() == type_t::lweight)
991  {
992  auto lw = down_pointer_cast<const lweight_t>(e);
993  res = lweight(lw->weight(), rweight(lw->sub(), w));
994  }
995 
996  // Trivial identity: (E<k>)<h> => E<kh>.
997  else if (ids_.is_trivial() && e->type() == type_t::rweight)
998  {
999  auto rw = down_pointer_cast<const rweight_t>(e);
1000  res = rweight(rw->sub(), weightset()->mul(rw->weight(), w));
1001  }
1002 
1003  // General case: E<k>.
1004  else
1005  res = std::make_shared<rweight_t>(w, e);
1006 
1007  return res;
1008  }
1009 
1010  /*--------------------------------------.
1011  | expressionset as a WeightSet itself. |
1012  `--------------------------------------*/
1013 
1014  DEFINE::is_zero(const value_t& v) const
1015  -> bool
1016  {
1017  return v->type() == type_t::zero;
1018  }
1019 
1020  DEFINE::is_one(const value_t& v)
1021  -> bool
1022  {
1023  return v->type() == type_t::one;
1024  }
1025 
1026  DEFINE::is_universal(const value_t& v) const
1027  -> bool
1028  {
1029  return (v->type() == type_t::complement
1030  && is_zero(down_pointer_cast<const complement_t>(v)->sub()));
1031  }
1032 
1034  -> size_t
1035  {
1036  return rat::size<self_t>(v);
1037  }
1038 
1039  DEFINE::compare(const value_t& lhs, const value_t& rhs)
1040  -> int
1041  {
1042  auto cmp = rat::compare<self_t>{};
1043  return cmp(lhs, rhs);
1044  }
1045 
1046  DEFINE::less(const value_t& lhs, const value_t& rhs)
1047  -> bool
1048  {
1049  return compare(lhs, rhs) < 0;
1050  }
1051 
1052  DEFINE::less_linear(const value_t& lhs, const value_t& rhs)
1053  -> bool
1054  {
1055  return less(unwrap_possible_lweight_(lhs),
1057  }
1058 
1059  DEFINE::equal(const value_t& lhs, const value_t& rhs)
1060  -> bool
1061  {
1062  return compare(lhs, rhs) == 0;
1063  }
1064 
1065  DEFINE::hash(const value_t& v)
1066  -> size_t
1067  {
1068  auto hasher = rat::hash<self_t>{};
1069  return hasher(v);
1070  }
1071 
1072  DEFINE::conv(const self_t& rs, const value_t& v) const
1073  -> value_t
1074  {
1075  if (ids_ == rs.ids_)
1076  return v;
1077  else
1078  return vcsn::rat::copy(rs, self(), v);
1079  }
1080 
1081  template <typename Context>
1082  template <typename GenSet>
1083  auto
1085  typename letterset<GenSet>::value_t v) const
1086  -> value_t
1087  {
1088  return atom(labelset()->conv(ls, v));
1089  }
1090 
1091  DEFINE::conv(b, typename b::value_t v) const
1092  -> value_t
1093  {
1094  return v ? one() : zero();
1095  }
1096 
1097  DEFINE::conv(const z& ws, typename z::value_t v) const
1098  -> value_t
1099  {
1100  return lweight(weightset()->conv(ws, v), one());
1101  }
1102 
1103  DEFINE::conv(const q& ws, typename q::value_t v) const
1104  -> value_t
1105  {
1106  return lweight(weightset()->conv(ws, v), one());
1107  }
1108 
1109  DEFINE::conv(const r& ws, typename r::value_t v) const
1110  -> value_t
1111  {
1112  return lweight(weightset()->conv(ws, v), one());
1113  }
1114 
1115  DEFINE::conv(const zmin& ws, typename zmin::value_t v) const
1116  -> value_t
1117  {
1118  return lweight(weightset()->conv(ws, v), one());
1119  }
1120 
1121  template <typename Context>
1122  template <typename Ctx2>
1123  auto
1126  const
1127  -> value_t
1128  {
1129  return vcsn::rat::copy(rs, self(), r);
1130  }
1131 
1132  DEFINE::conv(std::istream& is, bool) const
1133  -> value_t
1134  {
1135  // Our expression parser is written in dyn::, so we get a
1136  // dyn::expression that we down_cast.
1137  auto dynres = dyn::read_expression(context(), identities(), is);
1138  const auto& res = dynres->template as<self_t>();
1139  return res.value();
1140  }
1141 
1142  DEFINE::print(const value_t& v, std::ostream& o,
1143  format fmt) const
1144  -> std::ostream&
1145  {
1146  auto print = make_printer(self(), o);
1147  print.format(fmt);
1148  return print(v);
1149  }
1150 
1152  -> value_t
1153  {
1154  return vcsn::transpose(self(), v);
1155  }
1156 
1157  template <typename Context>
1158  template <typename... Args>
1159  auto
1161  -> value_t
1162  {
1163  return letter_class_<labelset_t>(std::forward<Args>(args)...,
1164  std::is_same<labelset_t, vcsn::oneset>{});
1165  }
1166 
1167  template <typename Context>
1168  template <typename LabelSet_>
1169  auto
1171  (std::set<std::pair<typename LabelSet_::letter_t,
1172  typename LabelSet_::letter_t>> ccs,
1173  bool accept,
1174  std::false_type) const
1175  -> value_t
1176  {
1177  auto res = zero();
1178  const auto& ls = *labelset();
1179  const auto gens = ls.generators();
1180  // FIXME: This piece of code screams for factoring. Yet, I want
1181  // to avoid useless costs such as building a empty/full set of
1182  // letters for [^].
1183 
1184  // [a-c].
1185  if (accept)
1186  for (const auto& cc: ccs)
1187  {
1188  auto i = std::find(std::begin(gens), std::end(gens), cc.first);
1189  auto end = std::find(i, std::end(gens), cc.second);
1190  VCSN_REQUIRE(end != std::end(gens),
1191  self(), ": invalid letter interval: ",
1192  to_string(*labelset(), ls.value(std::get<0>(cc))),
1193  '-',
1194  to_string(*labelset(), ls.value(std::get<1>(cc))));
1195  for (++end; i != end; ++i)
1196  // We want to avoid having (\z + a) + b in case of [ab].
1197  res = (is_zero(res)
1198  ? atom(ls.value(*i))
1199  : add(res, atom(ls.value(*i))));
1200  }
1201  // [^].
1202  else if (ccs.empty())
1203  for (auto l: gens)
1204  res = add(res, atom(ls.value(l)));
1205  // [^a-z].
1206  else
1207  {
1208  // Match the letters that are in no interval.
1209  std::set<typename LabelSet_::letter_t> accepted;
1210  for (const auto& cc: ccs)
1211  {
1212  auto i = std::find(std::begin(gens), std::end(gens), cc.first);
1213  auto end = std::find(i, std::end(gens), cc.second);
1214  VCSN_REQUIRE(end != std::end(gens),
1215  "invalid letter interval: ",
1216  to_string(*labelset(), ls.value(std::get<0>(cc))),
1217  '-',
1218  to_string(*labelset(), ls.value(std::get<1>(cc))));
1219  for (++end; i != end; ++i)
1220  accepted.emplace(*i);
1221  }
1222  for (auto c: gens)
1223  if (!has(accepted, c))
1224  // We want to avoid having (\z + c) in case of [^ab]
1225  // (considering lal_char(abc)).
1226  res = (is_zero(res)
1227  ? atom(ls.value(c))
1228  : add(res, atom(ls.value(c))));
1229  }
1230  require(!is_zero(res),
1231  "invalid empty letter class");
1232  return res;
1233  }
1234 
1235  template <typename Context>
1236  template <typename LabelSet_, typename... Args>
1237  auto
1239  std::true_type) const
1240  -> value_t
1241  {
1242  return one();
1243  }
1244 
1245 #undef DEFINE
1246 
1247 } // namespace rat
1248 } // namespace vcsn
Aut transpose(const transpose_automaton< Aut > &aut)
The transpose of a transpose automaton is the original automaton.
Definition: transpose.hh:253
static auto less(const value_t &l, const value_t &r) -> bool
Whether l < r.
Print as a parsable type string.
Definition: format.hh:26
static weight_t possibly_implicit_lweight_(const value_t &e)
The weight of e if it&#39;s an lweight, otherwise the weight one().
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:26
weightset_mixin< detail::r_impl > r
Definition: fwd.hh:54
bool is_valid(const Aut &aut)
Definition: is-valid.hh:139
auto concat_(const value_t &l, const value_t &r, std::true_type) const -> value_t
If labelset is wordset.
std::ostream & print(const Aut &aut, std::ostream &out=std::cout, const std::string &fmt="default")
Definition: print.hh:83
size_t size(const ExpSet &rs, const typename ExpSet::value_t &r)
static auto compare(const value_t &l, const value_t &r) -> int
Three-way comparison.
Implementation of labels are letters.
Definition: fwd.hh:10
typename weightset_t::value_t weight_t
identities_t identities() const
Accessor to the identities set.
auto add(const value_t &l, const value_t &r) const -> value_t
bool is_trivial() const
Whether trivial identities are on.
Definition: identities.hh:90
auto power(const value_t &e, unsigned n) const -> value_t
Add a power operator: e{n}.
An inner node with multiple children.
Definition: expression.hh:119
char eat(std::istream &is, char c)
Check lookahead character and advance.
Definition: stream.cc:147
const identities_t ids_
The set of rewriting rules to apply.
static auto equal(const value_t &l, const value_t &r) -> bool
Whether l == r.
static type_t type_ignoring_lweight_(const value_t &e)
The type of e, or the type of its child if e is a lweight.
bool is_agressive() const
Whether agressive optimizations are on.
Definition: identities.hh:66
expression read_expression(const context &ctx, identities ids, std::istream &is, const std::string &format="default", const location &loc=location{})
Read an expression from a stream.
Definition: read.cc:100
Request the set implementation (bool weights).
const context_t & context() const
Accessor to the context.
static auto zero() -> value_t
auto transpose(const value_t &e) const -> value_t
The transposed of this rational expression.
typename node_t::values_t values_t
A list (vector) of expressions.
letter_t value_t
Definition: letterset.hh:33
boost::flyweight< std::string, boost::flyweights::no_tracking, boost::flyweights::intermodule_holder > symbol
An internalized string.
Definition: symbol.hh:21
auto ldivide(const value_t &l, const value_t &r) const -> value_t
r`.
bool is_distributive() const
Whether distributive identities are on.
Definition: identities.hh:78
const labelset_ptr & labelset() const
Accessor to the labelset.
auto rdivide(const Aut1 &a1, const Aut2 &a2)
Compute the right quotient.
Definition: conjunction.hh:752
bool is_zero(const value_t &v) const ATTRIBUTE_PURE
Whether v is the \\z.
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:29
auto lweight(const weight_t &w, const value_t &e) const -> value_t
Left-multiplication by a weight.
auto tuple(Value &&... v) const -> value_t
Build a tuple: e | f | ....
static auto unwrap_possible_lweight_(const value_t &e) -> value_t
If e is an lweight, then its child, otherwise e.
An expressionset can implement several different sets of identities on expressions.
Definition: identities.hh:20
An input/output format for valuesets.
Definition: format.hh:13
Provide a variadic mul on top of a binary mul(), and one().
Definition: fwd.hh:46
Whether two contexts are composable.
Definition: traits.hh:107
printer< ExpSet > make_printer(const ExpSet &rs, std::ostream &out)
Definition: printer.hh:379
bool is_associative() const
Whether associative identities are on.
Definition: identities.hh:72
A functor for three-way comparison between two expressions.
Definition: compare.hh:18
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
Definition: pair.hh:252
auto as_tupleset() const -> std::enable_if_t< Ctx::is_lat, as_tupleset_t< Ctx >>
If we are multitape, ourself as a tupleset.
auto mul(const value_t &l, const value_t &r) const -> value_t
auto letter_class_(const Args &&... chars, std::true_type) const -> value_t
If labelset is oneset.
Whether some of the values evaluate as true.
Definition: tuple.hh:506
Definition: a-star.hh:8
auto in(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions arriving to state s.
Definition: automaton.hh:135
bool is_linear() const
Whether linear identities are on.
Definition: identities.hh:84
auto apply(Fun f, const std::tuple< Args... > &args) -> decltype(apply_impl_(f, args, make_index_sequence< sizeof...(Args)>()))
Unpack a tuple, and pass its content as argument to a funtion.
Definition: tuple.hh:242
typename node_t::value_t value_t
An expression (a shared pointer to a tree).
std::string to_string(identities i)
Wrapper around operator<<.
Definition: identities.cc:38
bool is_universal(const value_t &v) const ATTRIBUTE_PURE
Whether v is the 0{c}.
An inner node implementing a weight.
Definition: expression.hh:256
auto conv(const letterset< GenSet > &ls, typename letterset< GenSet >::value_t v) const -> value_t
ATTRIBUTE_NORETURN void raise_not_starrable(const WeightSet &ws, const typename WeightSet::value_t &w)
This value is not starrable.
Definition: weightset.hh:235
static bool is_one(const value_t &v) ATTRIBUTE_PURE
Whether v is the \\e.
return v
Definition: multiply.hh:362
static dyn::context ctx(const driver &d)
Get the context of the driver.
Definition: parse.cc:84
Turn a tuple of expressions that are labels into a multi-tape label.
symbol sname()
Definition: name.hh:65
const weightset_ptr & weightset() const
Accessor to the weightset.
Print for LaTeX.
Definition: format.hh:22
vs print_set(o)
static identities ids(const driver &d)
Get the identities of the driver.
Definition: parse.cc:91
void require(Bool b, Args &&... args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:87
auto complement(const value_t &e) const -> value_t
Add a complement operator: e{c}.
auto letter_class(Args &&... chars) const -> value_t
An expression matching one character amongst chars.
static auto atom(const label_t &v) -> value_t
Build a label.
static auto one() -> value_t
auto transposition(const value_t &e) const -> value_t
Add a transposition operator.
auto rweight(const value_t &e, const weight_t &w) const -> value_t
Right-multiplication by a weight.
#define VCSN_REQUIRE(Cond,...)
A macro similar to require.
Definition: raise.hh:98
std::enable_if_t< std::is_same< InExpSet, OutExpSet >{}, typename 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:189
#define BUILTIN_UNREACHABLE()
Definition: builtins.hh:13
expressionset_impl(const context_t &ctx, identities_t ids={})
Constructor.
int compare(const Lhs &lhs, const Rhs &rhs)
Comparison between lhs and rhs.
return res
Definition: multiply.hh:399
#define down_pointer_cast
Definition: cast.hh:16
return hasher(v)
auto print(const value_t &v, std::ostream &o=std::cout, format fmt={}) const -> std::ostream &
auto out(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions leaving state s.
Definition: automaton.hh:86