5 #include <boost/range/algorithm/find.hpp>
6 #include <boost/range/algorithm/lower_bound.hpp>
8 #include <vcsn/algos/fwd.hh>
17 #include <vcsn/dyn/fwd.hh>
30 template <
typename Context>
37 "series (currently) requires a commutative weightset product");
41 template <typename Context> \
43 expressionset_impl<Context>
53 DEFINE::make(std::istream& is)
57 eat(is,
"expressionset<");
58 auto ctx = Context::make(is);
70 DEFINE::print_set(std::ostream& o,
format fmt)
const
91 o << "expressionset<";
92 context().print_set(o, fmt);
94 if (identities() != vcsn::rat::identities{})
95 o << '(
' << identities() << ')
';
101 context().print_set(o, fmt);
103 if (identities() != vcsn::rat::identities{})
104 o << '(
' << identities() << ')
';
107 assert(!"expressionset::print_set: invalid format: rat");
113 DEFINE::open(bool o) const
116 return this->labelset()->open(o);
119 DEFINE::context() const -> const context_t&
124 DEFINE::identities() const -> identities_t
129 DEFINE::labelset() const -> const labelset_ptr&
131 return ctx_.labelset();
134 DEFINE::weightset() const -> const weightset_ptr&
136 return ctx_.weightset();
139 DEFINE::atom(const label_t& v)
142 if (labelset_t::is_one(v))
145 return std::make_shared<atom_t>(v);
151 return std::make_shared<zero_t>();
157 return std::make_shared<one_t>();
160 template <typename Context>
161 template <typename expressionset_impl<Context>::type_t Type>
163 expressionset_impl<Context>::gather_(values_t& res, const value_t& v) const
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));
173 template <typename Context>
174 template <typename expressionset_impl<Context>::type_t Type>
176 expressionset_impl<Context>::gather_(const value_t& l, const value_t& r) const
180 if (ids_.is_associative())
182 gather_<Type>(res, l);
183 gather_<Type>(res, r);
193 DEFINE::add(const value_t& l, const value_t& r) const
196 value_t res = nullptr;
199 res = std::make_shared<add_t>(l, r);
210 else if (ids_.is_linear())
211 res = add_linear_(l, r);
214 res = std::make_shared<add_t>(gather_<type_t::add>(l, r));
218 DEFINE::add_linear_(const add_t& s, const value_t& r) const
221 auto res = values_t{};
222 res.reserve(s.size() + 1);
224 // Copy the strictly smaller part.
225 auto i = boost::lower_bound(s, r, less_linear);
226 res.insert(end(res), begin(s), i);
232 if (less_linear(r, *i))
236 auto w = weightset()->add(possibly_implicit_lweight_(*i),
237 possibly_implicit_lweight_(r));
238 if (!weightset()->is_zero(w))
240 auto l = unwrap_possible_lweight_(*i);
241 res.emplace_back(lweight(w, l));
245 res.insert(end(res), i, end(s));
248 return add_(std::move(res));
251 DEFINE::add_(values_t&& vs) const
256 else if (vs.size() == 1)
259 return std::make_shared<add_t>(std::move(vs));
262 DEFINE::add_linear_(const add_t& s1, const add_t& s2) const
265 auto res = values_t{};
266 res.reserve(s1.size() + s2.size());
267 // Merge two increasing lists. Add weights of equal labels.
270 auto i1 = begin(s1), end1 = end(s1);
271 auto i2 = begin(s2), end2 = end(s2);
276 res.insert(res.end(), i2, end2);
281 res.insert(res.end(), i1, end1);
284 else if (less_linear(*i1, *i2))
285 res.emplace_back(*i1++);
286 else if (less_linear(*i2, *i1))
287 res.emplace_back(*i2++);
290 auto w = weightset()->add(possibly_implicit_lweight_(*i1),
291 possibly_implicit_lweight_(*i2));
292 if (!weightset()->is_zero(w))
294 auto l = unwrap_possible_lweight_(*i1);
295 res.emplace_back(lweight(w, l));
301 return add_(std::move(res));
304 DEFINE::add_linear_(const value_t& l, const value_t& r) const
309 value_t res = nullptr;
310 if (auto ls = std::dynamic_pointer_cast<const add_t>(l))
312 if (auto rs = std::dynamic_pointer_cast<const add_t>(r))
313 res = add_linear_(*ls, *rs);
315 res = add_linear_(*ls, r);
317 else if (auto rs = std::dynamic_pointer_cast<const add_t>(r))
318 res = add_linear_(*rs, l);
319 else if (less_linear(l, r))
320 res = std::make_shared<add_t>(l, r);
321 else if (less_linear(r, l))
322 res = std::make_shared<add_t>(r, l);
325 auto w = weightset()->add(possibly_implicit_lweight_(l),
326 possibly_implicit_lweight_(r));
327 res = lweight(w, unwrap_possible_lweight_(l));
332 DEFINE::type_ignoring_lweight_(const value_t& e)
335 return unwrap_possible_lweight_(e)->type();
338 DEFINE::possibly_implicit_lweight_(const value_t& e)
341 if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
344 return weightset_t::one();
347 DEFINE::unwrap_possible_lweight_(const value_t& e)
350 if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
356 DEFINE::mul(const value_t& l, const value_t& r) const
359 value_t res = nullptr;
362 res = std::make_shared<mul_t>(l, r);
372 // U_K: E.(<k>1) ⇒ E<k>, subsuming T: E.1 = E.
373 else if (type_ignoring_lweight_(r) == type_t::one)
374 res = rweight(l, possibly_implicit_lweight_(r));
376 // U_K: (<k>1).E ⇒ <k>E, subsuming T: 1.E = E.
377 else if (type_ignoring_lweight_(l) == type_t::one)
378 res = lweight(possibly_implicit_lweight_(l), r);
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))
386 lw = possibly_implicit_lweight_(l),
387 rw = possibly_implicit_lweight_(r);
389 nl = unwrap_possible_lweight_(l),
390 nr = unwrap_possible_lweight_(r);
391 res = lweight(weightset()->mul(lw, rw),
395 // (E+F)G => EG + FG.
396 else if (ids_.is_distributive() && l->type() == type_t::add)
399 // l is a sum, and r might be as well.
400 for (const auto& la: *down_pointer_cast<const add_t>(l))
401 res = add(res, mul(la, r));
404 // E(F+G) => EF + EG.
405 else if (ids_.is_distributive() && r->type() == type_t::add)
408 // r is a sum, l is not.
409 for (const auto& ra: *down_pointer_cast<const add_t>(r))
410 res = add(res, mul(l, ra));
414 res = std::make_shared<mul_t>(gather_<type_t::mul>(l, r));
418 DEFINE::compose(const value_t& l, const value_t& r) const
421 value_t res = nullptr;
424 res = std::make_shared<compose_t>(l, r);
434 // <k>1 @ <h>1 => <kh>1
435 else if (type_ignoring_lweight_(l) == type_t::one
436 && type_ignoring_lweight_(r) == type_t::one)
437 res = lweight(weightset()->mul(possibly_implicit_lweight_(l),
438 possibly_implicit_lweight_(r)),
443 res = std::make_shared<compose_t>(gather_<type_t::compose>(l, r));
447 DEFINE::conjunction(const value_t& l, const value_t& r) const
450 value_t res = nullptr;
453 res = std::make_shared<conjunction_t>(l, r);
464 else if (is_universal(r))
468 else if (is_universal(l))
471 // <k>1&<h>1 => <kh>1.
472 else if (type_ignoring_lweight_(l) == type_t::one
473 && type_ignoring_lweight_(r) == type_t::one)
474 res = lweight(weightset()->mul(possibly_implicit_lweight_(l),
475 possibly_implicit_lweight_(r)),
478 // <k>a&<h>a => <kh>a. <k>a&<h>b => 0.
479 else if (type_ignoring_lweight_(l) == type_t::atom
480 && type_ignoring_lweight_(r) == type_t::atom)
483 down_pointer_cast<const atom_t>(unwrap_possible_lweight_(l))->value();
485 down_pointer_cast<const atom_t>(unwrap_possible_lweight_(r))->value();
486 if (labelset()->equal(lhs, rhs))
487 res = rweight(l, possibly_implicit_lweight_(r));
492 // <k>1&<h>a => 0, <k>a&<h>1 => 0.
493 else if ((type_ignoring_lweight_(l) == type_t::one
494 && type_ignoring_lweight_(r) == type_t::atom)
495 || (type_ignoring_lweight_(l) == type_t::atom
496 && type_ignoring_lweight_(r) == type_t::one))
499 // General case: E & F.
501 res = std::make_shared<conjunction_t>(gather_<type_t::conjunction>(l, r));
505 DEFINE::ldivide(const value_t& l, const value_t& r) const
508 value_t res = nullptr;
511 res = std::make_shared<ldivide_t>(l, r);
526 res = std::make_shared<ldivide_t>(l, r);
530 DEFINE::rdivide(const value_t& l, const value_t& r) const
533 // l/r = (r{T} {\} l{T}){T}.
534 return transposition(ldivide(transposition(r), transposition(l)));
537 template <typename Context>
538 template <typename... Value>
539 auto expressionset_impl<Context>::tuple(Value&&... v) const
542 auto ts = as_tupleset();
543 auto t = ts.tuple(v...);
546 // FIXME: maybe we should introduce a short-circuiting version
547 // that would not make useless invocation when a \z was found.
548 if (detail::apply(any{},
549 detail::apply(([](const auto& rs, const auto& r)
550 { return rs.is_zero(r); }),
556 // If this is a tuple of labels, make it a (multitape) label.
557 // That allows algorithms such as standard, thompson, etc. to work
560 // Note that `\e|a` remains a tuple of expressions on lal x lal,
561 // but it is turned into a (multitape) label on lan x lal.
562 else if (tuple_of_label<>{self()}.is_label(t))
563 return atom(tuple_of_label<>{self()}.as_label(t));
565 return std::make_shared<tuple_t>(std::forward<Value>(v)...);
568 DEFINE::infiltrate(const value_t& l, const value_t& r) const
571 value_t res = nullptr;
574 res = std::make_shared<infiltrate_t>(l, r);
594 std::make_shared<infiltrate_t>(gather_<type_t::infiltrate>(l, r));
598 DEFINE::shuffle(const value_t& l, const value_t& r) const
601 value_t res = nullptr;
604 res = std::make_shared<shuffle_t>(l, r);
623 res = std::make_shared<shuffle_t>(gather_<type_t::shuffle>(l, r));
631 DEFINE::power(const value_t& e, unsigned n) const
634 value_t res = nullptr;
635 // Given E the expression s.t. E{n} = (<k>a){n}.
646 else if (ids_ && is_zero(e))
649 // Case: a == \e or a == <w>\e.
650 else if (ids_ && type_ignoring_lweight_(e) == type_t::one)
652 weight_t w = possibly_implicit_lweight_(e);
653 res = lweight(weightset()->power(w, n), one());
656 // Lweight in linear commutative: (<k>E){n} => <k{n}>(E{n}).
657 else if (ids_.is_linear()
658 && weightset()->is_commutative()
659 && e->type() == type_t::lweight)
661 const auto& lw = down_pointer_cast<const lweight_t>(e);
662 res = lweight(weightset()->power(lw->weight(), n),
663 power(lw->sub(), n));
666 // Sums in series: we have to distribute ([ab]{2} = aa+ab+ba+bb).
667 else if (ids_.is_distributive() && e->type() == type_t::add)
669 // FIXME: code duplication with weightset_mixin::power_.
671 for (unsigned i = 1; i < n; ++i)
675 // When associative, instead of repeated multiplication,
676 // immediately create n copies of E.
677 else if (ids_.is_associative())
678 res = std::make_shared<mul_t>(n, e);
680 // Default case: E{n} = ((..(EE)...)E.
683 // FIXME: code duplication with weightset_mixin::power_.
685 for (unsigned i = 1; i < n; ++i)
692 DEFINE::concat(const value_t& l, const value_t& r) const
695 // A static dispatch is needed, as the product of labels is not
696 // available if not LAW.
697 return concat_(l, r, typename is_law<Context>::type{});
700 // Concatenation when not LAW.
701 DEFINE::concat_(const value_t& l, const value_t& r, std::false_type) const
707 // Concatenation when LAW.
708 DEFINE::concat_(const value_t& l, const value_t& r, std::true_type) const
711 // concat((ab).<2>c, d.(ef)) = (ab).<2>(cd).(ef).
713 // Store (ab) in expression, then concat(<2>c, d) if c and d are
714 // atoms, otherwise <2>c then d, then (ef).
715 if ((type_ignoring_lweight_(l) == type_t::atom || l->type() == type_t::mul)
716 && (r->type() == type_t::atom || r->type() == type_t::mul))
720 gather_<type_t::mul>(ls, l);
723 gather_<type_t::mul>(rs, r);
725 // FIXME: we should perform that "if" with the one above, and
726 // enter this section only if we really are going to concat.
727 // This would avoid the "else" clause.
728 if (type_ignoring_lweight_(ls.back()) == type_t::atom
729 && rs.front()->type() == type_t::atom)
731 // Fetch weight and atom of the last lhs.
732 auto w = possibly_implicit_lweight_(ls.back());
734 = std::dynamic_pointer_cast<const atom_t>
735 (unwrap_possible_lweight_(ls.back()));
736 // Fetch atom of the first rhs.
737 auto rhs = std::dynamic_pointer_cast<const atom_t>(rs.front());
740 atom(labelset()->mul(lhs->value(), rhs->value())));
741 ls.insert(ls.end(), rs.begin() + 1, rs.end());
744 ls.insert(ls.end(), rs.begin(), rs.end());
748 return std::make_shared<mul_t>(std::move(ls));
751 // Handle all the trivial identities.
755 DEFINE::star(const value_t& e) const
758 value_t res = nullptr;
761 if (ids_ && is_zero(e))
766 res = std::make_shared<star_t>(e);
767 if (ids_.is_distributive() && !is_valid(*this, res))
768 raise_not_starrable(self(), e);
774 DEFINE::complement(const value_t& e) const
777 value_t res = nullptr;
780 res = std::make_shared<complement_t>(e);
782 // The following identities make derived-term (<2>a)*{c} terminate.
783 // (<k>E){c} => E{c}.
784 else if (auto w = std::dynamic_pointer_cast<const lweight_t>(e))
785 res = complement(w->sub());
787 // (E<k>){c} => E{c}.
788 else if (auto w = std::dynamic_pointer_cast<const rweight_t>(e))
789 res = complement(w->sub());
791 // E{c}{c} => E if on B or F2.
793 // Indeed, (<2>a)*{c}{c} is actually denoting a*, not (<2>a)*.
794 else if (e->type() == type_t::complement
795 && std::is_same<weight_t, bool>{})
796 res = down_pointer_cast<const complement_t>(e)->sub();
799 res = std::make_shared<complement_t>(e);
804 DEFINE::transposition(const value_t& e) const
807 value_t res = nullptr;
810 res = std::make_shared<transposition_t>(e);
820 // a{T} => a, (abc){T} => cba.
821 else if (auto l = std::dynamic_pointer_cast<const atom_t>(e))
822 res = atom(labelset()->transpose(l->value()));
825 res = std::make_shared<transposition_t>(e);
833 DEFINE::lweight(const weight_t& w, const value_t& e) const
836 value_t res = nullptr;
839 res = std::make_shared<lweight_t>(w, e);
841 // <k>0 => 0, <1>E => E.
842 else if (is_zero(e) || weightset()->is_one(w))
846 else if (weightset()->is_zero(w))
849 // <k>(<h>E) => <kh>E.
850 else if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
851 res = lweight(weightset()->mul(w, lw->weight()), lw->sub());
853 // Distributive: <k>(E+F) => <k>E + <k>F.
854 else if (ids_.is_distributive() && e->type() == type_t::add)
856 const auto& s = down_pointer_cast<const add_t>(e);
857 // We can build the result faster by emplace_back'ing addends without
860 for (
const auto& a: *s)
861 addends.emplace_back(
lweight(w, a));
862 res = std::make_shared<add_t>(std::move(addends));
867 res = std::make_shared<lweight_t>(w, e);
878 res = std::make_shared<rweight_t>(w, e);
881 else if (ids_.is_linear() && weightset()->is_commutative())
885 else if (weightset()->is_zero(w))
889 else if (weightset()->is_one(w))
892 else if (e->is_leaf())
897 else if (
auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
901 else if (
auto rw = std::dynamic_pointer_cast<const rweight_t>(e))
902 res =
rweight(rw->sub(), weightset()->mul(rw->weight(), w));
906 res = std::make_shared<rweight_t>(w, e);
931 && is_zero(down_pointer_cast<const complement_t>(
v)->sub()));
937 return rat::size<self_t>(
v);
950 return less(unwrap_possible_lweight_(lhs),
951 unwrap_possible_lweight_(rhs));
957 return !
less(lhs, rhs) && !
less(rhs, lhs);
976 template <
typename Context>
977 template <
typename GenSet>
1016 template <
typename Context>
1017 template <
typename Ctx2>
1033 const auto&
res = dynres->template as<self_t>();
1052 template <
typename Context>
1053 template <
typename... Args>
1058 return letter_class_<labelset_t>(std::forward<Args>(args)...,
1059 std::is_same<labelset_t, vcsn::oneset>{});
1062 template <
typename Context>
1063 template <
typename LabelSet_>
1067 typename LabelSet_::letter_t>> ccs,
1069 std::false_type)
const
1072 using boost::range::find;
1074 const auto& ls = *labelset();
1075 auto gens = ls.generators();
1082 for (
const auto& cc: ccs)
1084 auto i = find(gens, cc.first);
1085 auto end = std::find(i, std::end(gens), cc.second);
1087 self(),
": invalid letter interval: ",
1088 to_string(*labelset(), ls.value(std::get<0>(cc))),
1090 to_string(*labelset(), ls.value(std::get<1>(cc))));
1091 for (++end; i != end; ++i)
1094 ?
atom(ls.value(*i))
1095 :
add(res,
atom(ls.value(*i))));
1098 else if (ccs.empty())
1100 res =
add(res,
atom(ls.value(l)));
1105 std::set<typename LabelSet_::letter_t> accepted;
1106 for (
const auto& cc: ccs)
1108 auto i = find(gens, cc.first);
1109 auto end = std::find(i, std::end(gens), cc.second);
1111 "invalid letter interval: ",
1112 to_string(*labelset(), ls.value(std::get<0>(cc))),
1114 to_string(*labelset(), ls.value(std::get<1>(cc))));
1115 for (++end; i != end; ++i)
1116 accepted.emplace(*i);
1119 if (!
has(accepted, c))
1124 :
add(res,
atom(ls.value(c))));
1127 "invalid empty letter class");
1131 template <
typename Context>
1132 template <
typename LabelSet_,
typename... Args>
1135 std::true_type) const
const weightset_ptr & weightset() const
Accessor to the weightset.
bool is_distributive() const
Whether distributive.
variadic< type_t::add, Context > add
Aut transpose(const transpose_automaton< Aut > &aut)
boost::flyweight< std::string, boost::flyweights::no_tracking, boost::flyweights::intermodule_holder > symbol
An internalized string.
Implementation of labels are letters.
std::ostream & print_set(std::ostream &o, format fmt={}) const
size_t size(const ExpSet &rs, const typename ExpSet::value_t &r)
auto conv(const letterset< GenSet > &ls, typename letterset< GenSet >::value_t v) const -> value_t
A functor to check whether one rational expression is (strictly) less than another one...
Print as a parsable type string.
auto letter_class_(const Args &&...chars, std::true_type) const -> value_t
If labelset is oneset.
void require(Bool b, Args &&...args)
If b is not verified, raise an error with args as message.
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.
constant< type_t::one, Context > one
weight_node< type_t::rweight, Context > rweight
typename node_t::value_t value_t
An expression (a shared pointer to a tree).
ATTRIBUTE_PURE bool has(const boost::container::flat_set< Key, Compare, Allocator > &s, const Key &e)
Whether e is member of s.
std::ostream & print(const Aut &aut, std::ostream &out=std::cout, const std::string &fmt="default")
typename node_t::values_t values_t
A list (vector) of expressions.
auto letter_class(Args &&...chars) const -> value_t
An expression matching one character amongst chars.
#define VCSN_REQUIRE(Cond,...)
A macro similar to require.
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
An input/output format for valuesets.
const identities_t ids_
The set of rewriting rules to apply.
An expressionset can implement several different sets of identities on expressions.
constant< type_t::zero, Context > zero
expressionset_impl(const context_t &ctx, identities_t ids={})
Constructor.
Request the set implementation (bool weights).
std::string to_string(identities i)
Wrapper around operator<<.
expression read_expression(const context &ctx, identities ids, std::istream &is, const std::string &format="default")
Read an expression from a stream.
typename weightset_t::value_t weight_t
weight_node< type_t::lweight, Context > lweight
static identities ids(const driver &d)
Get the identities of the driver.
Provide a variadic mul on top of a binary mul(), and one().
char eat(std::istream &is, char c)
Check lookahead character and advance.
OutExpSet::value_t copy(const InExpSet &in_rs, const OutExpSet &out_rs, const typename InExpSet::value_t &v)
Copy/convert a rational expression.
printer< ExpSet > make_printer(const ExpSet &rs, std::ostream &out)
static dyn::context ctx(const driver &d)
Get the context of the driver.