// -*- coding: utf-8 -*- // Copyright (C) 2014, 2015 Laboratoire de Recherche et Developpement de // l'Epita (LRDE). // // This file is part of Spot, a model checking library. // // Spot is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // Spot is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public // License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include #include "ltlast/allnodes.hh" #include "ltlvisit/apcollect.hh" #include "ltlvisit/clone.hh" #include "ltlvisit/mutation.hh" #include "ltlvisit/length.hh" #include "misc/hash.hh" #define Implies_(x, y) \ spot::ltl::binop::instance(spot::ltl::binop::Implies, (x), (y)) #define And_(x, y) \ spot::ltl::multop::instance(spot::ltl::multop::And, (x), (y)) #define AndRat_(x, y) \ spot::ltl::multop::instance(spot::ltl::multop::AndRat, (x), (y)) #define AndNLM_(x) \ spot::ltl::multop::instance(spot::ltl::multop::AndNLM, (x)) #define Concat_(x, y) \ spot::ltl::multop::instance(spot::ltl::multop::Concat, (x), (y)) #define Not_(x) \ spot::ltl::unop::instance(spot::ltl::unop::Not, (x)) namespace spot { namespace ltl { namespace { class replace_visitor : public clone_visitor { public: void visit(const atomic_prop* ap) { if (ap == ap1_) result_ = ap2_->clone(); else result_ = ap->clone(); } const formula* replace(const formula* f, const atomic_prop* ap1, const atomic_prop* ap2) { ap1_ = ap1; ap2_ = ap2; return recurse(f); } private: const atomic_prop* ap1_; const atomic_prop* ap2_; }; typedef std::vector vec; class mutation_visitor : public clone_visitor { public: mutation_visitor(const formula* f, unsigned opts) : f_(f), opts_(opts) { } void visit(const atomic_prop* ap) { result_ = 0; if (opts_ & Mut_Ap2Const) { if (mutation_counter_-- == 0) result_ = constant::true_instance(); if (mutation_counter_-- == 0) result_ = constant::false_instance(); } if (!result_) result_ = ap->clone(); } void visit(const unop* uo) { result_ = 0; if (opts_ & Mut_Remove_Ops) { if ((uo->op() == unop::G || uo->op() == unop::F || uo->op() == unop::X || uo->op() == unop::Not) && mutation_counter_-- == 0) result_ = uo->child()->clone(); } if (!result_) { if (mutation_counter_ < 0) result_ = uo->clone(); else { result_ = unop::instance(uo->op(), recurse(uo->child())); } } } void visit(const binop* bo) { const formula* first = bo->first(); const formula* second = bo->second(); result_ = 0; if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) result_ = first->clone(); if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) result_ = second->clone(); if (opts_ & Mut_Rewrite_Ops) { switch (bo->op()) { case binop::U: if (mutation_counter_-- == 0) result_ = binop::instance(binop::W, first->clone(), second->clone()); break; case binop::M: if (mutation_counter_-- == 0) result_ = binop::instance(binop::R, first->clone(), second->clone()); if (mutation_counter_-- == 0) result_ = binop::instance(binop::U, second->clone(), first->clone()); break; case binop::R: if (mutation_counter_-- == 0) result_ = binop::instance(binop::W, second->clone(), first->clone()); break; default: break; } } if (opts_ & Mut_Split_Ops) { switch (bo->op()) { case binop::Equiv: if (mutation_counter_-- == 0) result_ = Implies_(first->clone(), second->clone()); if (mutation_counter_-- == 0) result_ = Implies_(second->clone(), first->clone()); if (mutation_counter_-- == 0) result_ = And_(first->clone(), second->clone()); if (mutation_counter_-- == 0) { // Negate the two argument sequentially (in this // case right before left, otherwise different // compilers will make different choices. auto right = Not_(second->clone()); result_ = And_(Not_(first->clone()), right); } break; case binop::Xor: if (mutation_counter_-- == 0) result_ = And_(first->clone(), Not_(second->clone())); if (mutation_counter_-- == 0) result_ = And_(Not_(first->clone()), second->clone()); break; default: break; } } if (!result_) { if (mutation_counter_ < 0) { result_ = bo->clone(); } else { // For historical reasons, we evaluate the right // side before the left one. The other order would // be OK as well but require changing the test // suite. Evaluating both sides during the call to // instance() is incorrect, because each compiler // could decide of a different order. auto right = recurse(second); result_ = binop::instance(bo->op(), recurse(first), right); } } } void visit(const bunop* bu) { const formula* c = bu->child()->clone(); result_ = 0; if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0) result_ = c->clone(); if (opts_ & Mut_Simplify_Bounds) { if (bu->min() > 0 && mutation_counter_-- == 0) result_ = bunop::instance(bu->op(), c, bu->min() - 1, bu->max()); if (bu->min() > 0 && mutation_counter_-- == 0) result_ = bunop::instance(bu->op(), c, 0, bu->max()); if (bu->max() != bunop::unbounded && bu->max() > bu->min() && mutation_counter_-- == 0) result_ = bunop::instance(bu->op(), c, bu->min(), bu->max() - 1); if (bu->max() != bunop::unbounded && mutation_counter_-- == 0) result_ = bunop::instance(bu->op(), c, bu->min(), bunop::unbounded); } if (!result_) { c->destroy(); if (mutation_counter_ < 0) result_ = bu->clone(); else result_ = bunop::instance(bu->op(), recurse(c), bu->min(), bu->max()); } } void visit(const multop* mo) { int mos = mo->size(); int i; result_ = 0; if (opts_ & Mut_Remove_Multop_Operands) { for (i = 0; i < mos; ++i) if (mutation_counter_-- == 0) result_ = mo->all_but(i); } if (opts_ & Mut_Split_Ops && mo->op() == multop::AndNLM) { if (mutation_counter_ >= 0 && mutation_counter_ < 2 * (mos - 1)) { vec* v1 = new vec(); vec* v2 = new vec(); v1->push_back(mo->nth(0)->clone()); bool reverse = false; i = 1; while (i < mos) { if (mutation_counter_-- == 0) break; if (mutation_counter_-- == 0) { reverse = true; break; } v1->push_back(mo->nth(i++)->clone()); } for (; i < mos; ++i) v2->push_back(mo->nth(i)->clone()); const formula* tstar = bunop::instance(bunop::Star, constant::true_instance(), 0, bunop::unbounded); const formula* first = AndNLM_(v1); const formula* second = AndNLM_(v2); if (!reverse) result_ = AndRat_(Concat_(first, tstar), second); else result_ = AndRat_(Concat_(second, tstar), first); } else mutation_counter_ -= 2 * (mos - 1); } if (!result_) { if (mutation_counter_ < 0) result_ = mo->clone(); else { vec* v = new vec(); for (i = 0; i < mos; ++i) v->push_back(recurse(mo->nth(i))); result_ = multop::instance(mo->op(), v); } } } const formula* recurse(const formula* f) { f->accept(*this); return result_; } const formula* get_mutation(int n) { mutation_counter_ = n; const formula* mut = recurse(f_); if (mut == f_) { mut->destroy(); return 0; } return mut; } private: const formula* f_; int mutation_counter_ = 0; unsigned opts_; }; bool formula_length_less_than(const formula* left, const formula* right) { assert(left); assert(right); if (left == right) return false; return length(left) < length(right); } typedef std::set fset_t; void single_mutation_rec(const formula* f, fset_t& mutations, unsigned opts, unsigned& n, unsigned m) { if (m == 0) { if (mutations.insert(f).second) { f->clone(); --n; } } else { const formula* mut(nullptr); int i = 0; mutation_visitor mv(f, opts); while (n > 0 && (mut = mv.get_mutation(i++))) { single_mutation_rec(mut, mutations, opts, n, m - 1); mut->destroy(); } } } void replace_ap_rec(const formula* f, fset_t& mutations, unsigned opts, unsigned& n, unsigned m) { if (m == 0) { if (mutations.insert(f).second) { f->clone(); --n; } } else { if (!n) return; replace_visitor rv; auto aps = std::unique_ptr(atomic_prop_collect(f)); for (auto ap1: *aps) for (auto ap2: *aps) { if (ap1 == ap2) continue; auto mut = rv.replace(f, ap1, ap2); replace_ap_rec(mut, mutations, opts, n, m - 1); mut->destroy(); if (!n) return; } } } } std::vector mutate(const formula* f, unsigned opts, unsigned max_output, unsigned mutation_count, bool sort) { fset_t mutations; single_mutation_rec(f, mutations, opts, max_output, mutation_count); if (opts & Mut_Remove_One_Ap) replace_ap_rec(f, mutations, opts, max_output, mutation_count); vec res(mutations.begin(), mutations.end()); if (sort) std::sort(res.begin(), res.end(), formula_length_less_than); return res; } } }