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);
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 712 // concat((ab).<2>c, d.(ef)) = (ab).<2>(cd).(ef). 714 // Store (ab) in expression, then concat(<2>c, d) if c and d are 715 // atoms, otherwise <2>c then d, then (ef). 716 if ((type_ignoring_lweight_(l) == type_t::atom || l->type() == type_t::mul) 717 && (r->type() == type_t::atom || r->type() == type_t::mul)) 721 gather_<type_t::mul>(ls, l); 724 gather_<type_t::mul>(rs, r); 726 // FIXME: we should perform that "if" with the one above, and 727 // enter this section only if we really are going to concat. 728 // This would avoid the "else" clause. 729 if (type_ignoring_lweight_(ls.back()) == type_t::atom 730 && rs.front()->type() == type_t::atom) 732 // Fetch atom of the last lhs. 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()); 739 auto product = atom(labelset()->mul(lhs->value(), rhs->value())); 741 if (ls.back()->type() == type_t::lweight) 743 const auto& lw = down_pointer_cast<const lweight_t>(ls.back()); 744 product = lweight(lw->weight(), product); 748 ls.insert(ls.end(), rs.begin() + 1, rs.end()); 751 ls.insert(ls.end(), rs.begin(), rs.end()); 755 return std::make_shared<mul_t>(std::move(ls)); 758 // Handle all the trivial identities. 762 DEFINE::star(const value_t& e) const 765 value_t res = nullptr; 768 if (ids_ && is_zero(e)) 771 // E** => E* if on B. 772 else if (ids_.is_agressive() 773 && e->type() == type_t::star 774 && std::is_same<weightset_t, b>{}) 779 res = std::make_shared<star_t>(e); 780 if (ids_.is_distributive() && !is_valid(*this, res)) 781 raise_not_starrable(self(), e); 787 DEFINE::complement(const value_t& e) const 790 value_t res = nullptr; 793 res = std::make_shared<complement_t>(e); 795 // The following identities make derived-term (<2>a)*{c} terminate. 796 // (<k>E){c} => E{c}. 797 else if (auto w = std::dynamic_pointer_cast<const lweight_t>(e)) 798 res = complement(w->sub()); 800 // (E<k>){c} => E{c}. 801 else if (auto w = std::dynamic_pointer_cast<const rweight_t>(e)) 802 res = complement(w->sub()); 804 // E{c}{c} => E if on B or F2. 806 // Indeed, (<2>a)*{c}{c} is actually denoting a*, not (<2>a)*. 807 else if (e->type() == type_t::complement 808 && std::is_same<weight_t, bool>{}) 809 res = down_pointer_cast<const complement_t>(e)->sub(); 812 res = std::make_shared<complement_t>(e); 817 DEFINE::transposition(const value_t& e) const 820 value_t res = nullptr; 823 res = std::make_shared<transposition_t>(e); 825 // E{T} => E{t} when agressive. 826 else if (ids_.is_agressive()) 837 // a{T} => a, (abc){T} => cba. 838 else if (auto l = std::dynamic_pointer_cast<const atom_t>(e)) 839 res = atom(labelset()->transpose(l->value())); 841 // E{T}{T} => E, if agressive. 842 else if (ids_.is_agressive() 843 && e->type() == type_t::transposition) 844 res = down_pointer_cast<const transposition_t>(e)->sub(); 847 res = std::make_shared<transposition_t>(e); 855 DEFINE::lweight(const weight_t& w, const value_t& e) const 858 value_t res = nullptr; 861 res = std::make_shared<lweight_t>(w, e); 863 // <k>0 => 0, <1>E => E. 864 else if (is_zero(e) || weightset()->is_one(w)) 868 else if (weightset()->is_zero(w)) 871 // <k>(<h>E) => <kh>E. 872 else if (auto lw = std::dynamic_pointer_cast<const lweight_t>(e)) 873 res = lweight(weightset()->mul(w, lw->weight()), lw->sub()); 875 // Distributive: <k>(E+F) => <k>E + <k>F. 876 else if (ids_.is_distributive() && e->type() == type_t::add) 878 const auto& s = down_pointer_cast<const add_t>(e); 879 // We can build the result faster by emplace_back'ing addends without
882 for (
const auto& a: *s)
883 addends.emplace_back(
lweight(w, a));
884 res = std::make_shared<add_t>(std::move(addends));
889 res = std::make_shared<lweight_t>(w, e);
900 res = std::make_shared<rweight_t>(w, e);
914 else if (e->is_leaf())
919 else if (
auto lw = std::dynamic_pointer_cast<const lweight_t>(e))
923 else if (
auto rw = std::dynamic_pointer_cast<const rweight_t>(e))
928 res = std::make_shared<rweight_t>(w, e);
953 &&
is_zero(down_pointer_cast<const complement_t>(
v)->sub()));
959 return rat::size<self_t>(
v);
966 return cmp(lhs, rhs);
1004 template <
typename Context>
1005 template <
typename GenSet>
1044 template <
typename Context>
1045 template <
typename Ctx2>
1061 const auto&
res = dynres->template as<self_t>();
1080 template <
typename Context>
1081 template <
typename... Args>
1086 return letter_class_<labelset_t>(std::forward<Args>(args)...,
1087 std::is_same<labelset_t, vcsn::oneset>{});
1090 template <
typename Context>
1091 template <
typename LabelSet_>
1095 typename LabelSet_::letter_t>> ccs,
1097 std::false_type)
const 1102 auto gens = ls.generators();
1109 for (
const auto& cc: ccs)
1111 auto i = std::find(std::begin(gens), std::end(gens), cc.first);
1112 auto end = std::find(i, std::end(gens), cc.second);
1114 self(),
": invalid letter interval: ",
1118 for (++end; i != end; ++i)
1121 ?
atom(ls.value(*i))
1122 :
add(res,
atom(ls.value(*i))));
1125 else if (ccs.empty())
1127 res =
add(res,
atom(ls.value(l)));
1132 std::set<typename LabelSet_::letter_t> accepted;
1133 for (
const auto& cc: ccs)
1135 auto i = std::find(std::begin(gens), std::end(gens), cc.first);
1136 auto end = std::find(i, std::end(gens), cc.second);
1138 "invalid letter interval: ",
1142 for (++end; i != end; ++i)
1143 accepted.emplace(*i);
1146 if (!
has(accepted, c))
1151 :
add(res,
atom(ls.value(c))));
1154 "invalid empty letter class");
1158 template <
typename Context>
1159 template <
typename LabelSet_,
typename... Args>
1162 std::true_type)
const auto zero() const -> value_t
auto letter_class_(const Args &&... chars, std::true_type) const -> value_t
If labelset is oneset.
Print as a parsable type string.
ATTRIBUTE_PURE bool has(const boost::container::flat_set< Key, Compare, Allocator > &s, const Key &e)
Whether e is member of s.
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.
typename node_t::values_t values_t
A list (vector) of expressions.
printer< ExpSet > make_printer(const ExpSet &rs, std::ostream &out)
typename weightset_t::value_t weight_t
identities_t identities() const
Accessor to the identities set.
const identities_t ids_
The set of rewriting rules to apply.
bool is_linear() const
Whether linear.
static auto atom(const label_t &v) -> value_t
Build a label.
expressionset_impl(const context_t &ctx, identities_t ids={})
Constructor.
An expressionset can implement several different sets of identities on expressions.
bool is_distributive() const
Whether distributive.
static auto one() -> value_t
boost::flyweight< std::string, boost::flyweights::no_tracking, boost::flyweights::intermodule_holder > symbol
An internalized string.
typename node_t::value_t value_t
An expression (a shared pointer to a tree).
static dyn::context ctx(const driver &d)
Get the context of the driver.
pair_automaton< Aut > pair(const Aut &aut, bool keep_initials=false)
static identities ids(const driver &d)
Get the identities of the driver.
std::string to_string(identities i)
Wrapper around operator<<.
auto add(const value_t &l, const value_t &r) const -> value_t
auto conv(const ValueSet &vs, const std::string &str, Args &&... args) -> decltype(vs.conv(std::declval< std::istream &>(), std::forward< Args >(args)...))
Parse str via vs.conv.
const context_t & context() const
Accessor to the context.
OutExpSet::value_t copy(const InExpSet &in_rs, const OutExpSet &out_rs, const typename InExpSet::value_t &v)
Copy/convert a rational expression.
const labelset_ptr & labelset() const
Accessor to the labelset.
static auto less(const value_t &l, const value_t &r) -> bool
Whether l < r.
auto conv(const letterset< GenSet > &ls, typename letterset< GenSet >::value_t v) const -> value_t
auto letter_class(Args &&... chars) const -> value_t
An expression matching one character amongst chars.
An input/output format for valuesets.
Provide a variadic mul on top of a binary mul(), and one().
static auto compare(const value_t &l, const value_t &r) -> int
Three-way comparison.
Aut transpose(const transpose_automaton< Aut > &aut)
The transpose of a transpose automaton is the original automaton.
A functor for three-way comparison between two expressions.
auto print(const value_t &v, std::ostream &o=std::cout, format fmt={}) const -> std::ostream &
static auto unwrap_possible_lweight_(const value_t &e) -> value_t
If e is an lweight, then its child, otherwise e.
std::ostream & print(const Aut &aut, std::ostream &out=std::cout, const std::string &fmt="default")
Implementation of labels are letters.
char eat(std::istream &is, char c)
Check lookahead character and advance.
auto lweight(const weight_t &w, const value_t &e) const -> value_t
Left-multiplication by a weight.
#define VCSN_REQUIRE(Cond,...)
A macro similar to require.
void require(Bool b, Args &&... args)
If b is not verified, raise an error with args as message.
Request the set implementation (bool weights).
const weightset_ptr & weightset() const
Accessor to the weightset.
int compare(const Lhs &lhs, const Rhs &rhs)
Comparison between lhs and rhs.
size_t size(const ExpSet &rs, const typename ExpSet::value_t &r)
auto rweight(const value_t &e, const weight_t &w) const -> value_t
Right-multiplication by a weight.
bool is_zero(const value_t &v) const ATTRIBUTE_PURE
Whether v is the \\z.