spot/src/ltlast/bunop.cc
Alexandre Duret-Lutz ba5aff2460 Replace << "c" by << 'c', and check for it in style.sh
* src/sanity/style.test: Add a test.
* iface/dve2/dve2.cc, iface/dve2/dve2check.cc, src/bin/common_output.cc,
src/bin/dstar2tgba.cc, src/bin/ltl2tgba.cc, src/bin/ltlcross.cc,
src/dstarparse/dra2ba.cc, src/dstarparse/fmterror.cc,
src/dstarparse/nsa2tgba.cc, src/kripke/kripkeprint.cc,
src/kripkeparse/fmterror.cc, src/ltlast/atomic_prop.cc,
src/ltlast/bunop.cc, src/ltltest/ltlrel.cc, src/ltltest/reduc.cc,
src/ltltest/syntimpl.cc, src/ltlvisit/dotty.cc, src/ltlvisit/lbt.cc,
src/ltlvisit/randomltl.cc, src/ltlvisit/relabel.cc,
src/ltlvisit/simplify.cc, src/ltlvisit/tostring.cc, src/misc/bitvect.cc,
src/misc/optionmap.cc, src/misc/timer.cc, src/neverparse/fmterror.cc,
src/priv/freelist.cc, src/saba/sabacomplementtgba.cc,
src/sabaalgos/sabadotty.cc, src/taalgos/dotty.cc,
src/taalgos/minimize.cc, src/tgba/bdddict.cc, src/tgba/bddprint.cc,
src/tgba/futurecondcol.cc, src/tgba/taatgba.hh,
src/tgba/tgbakvcomplement.cc, src/tgba/tgbasafracomplement.cc,
src/tgbaalgos/compsusp.cc, src/tgbaalgos/cycles.cc,
src/tgbaalgos/dotty.cc, src/tgbaalgos/dtbasat.cc,
src/tgbaalgos/dtgbasat.cc, src/tgbaalgos/emptiness.cc,
src/tgbaalgos/gtec/gtec.cc, src/tgbaalgos/gv04.cc,
src/tgbaalgos/lbtt.cc, src/tgbaalgos/ltl2tgba_fm.cc,
src/tgbaalgos/minimize.cc, src/tgbaalgos/neverclaim.cc,
src/tgbaalgos/powerset.cc, src/tgbaalgos/replayrun.cc,
src/tgbaalgos/save.cc, src/tgbaalgos/scc.cc, src/tgbaalgos/sccfilter.cc,
src/tgbaalgos/weight.cc, src/tgbaalgos/word.cc,
src/tgbaparse/fmterror.cc, src/tgbatest/bitvect.cc,
src/tgbatest/complementation.cc, src/tgbatest/intvcmp2.cc,
src/tgbatest/intvcomp.cc, src/tgbatest/ltl2tgba.cc,
src/tgbatest/randtgba.cc: Replace << "c" by << 'c' when
appropriate.
2014-02-12 16:17:51 +01:00

312 lines
7.1 KiB
C++

// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Laboratoire de
// Recherche et Développement 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 <http://www.gnu.org/licenses/>.
#include "config.h"
#include "bunop.hh"
#include "visitor.hh"
#include <cassert>
#include <iostream>
#include <sstream>
#include "constant.hh"
#include "unop.hh"
#include "multop.hh"
namespace spot
{
namespace ltl
{
// Can't build it on startup, because it uses
// constant::true_instance that may not have been built yet...
const formula* bunop::one_star_ = 0;
bunop::bunop(type op, const formula* child, unsigned min, unsigned max)
: ref_formula(BUnOp), op_(op), child_(child), min_(min), max_(max)
{
props = child->get_props();
assert(is.sere_formula);
is.boolean = false;
is.ltl_formula = false;
is.eltl_formula = false;
is.psl_formula = false;
is.eventual = false;
is.universal = false;
is.syntactic_safety = false;
is.syntactic_guarantee = false;
is.syntactic_obligation = false;
is.syntactic_recurrence = false;
is.syntactic_persistence = false;
switch (op_)
{
case Star:
if (max_ == unbounded)
is.finite = false;
if (min_ == 0)
is.accepting_eword = true;
break;
}
}
bunop::~bunop()
{
// one_star_ should never get deleted. Otherwise, that means it
// has been destroyed too much, or not cloned enough.
assert(this != one_star_);
// Get this instance out of the instance map.
size_t c = instances.erase(key(op(), child(), min_, max_));
assert(c == 1);
(void) c; // For the NDEBUG case.
// Dereference child.
child()->destroy();
}
std::string
bunop::dump() const
{
std::ostringstream out;
out << "bunop(" << op_name() << ", "
<< child()->dump() << ", " << min_ << ", ";
if (max_ == unbounded)
out << "unbounded";
else
out << max_;
out << ')';
return out.str();
}
void
bunop::accept(visitor& v) const
{
v.visit(this);
}
const char*
bunop::op_name() const
{
switch (op_)
{
case Star:
return "Star";
}
// Unreachable code.
assert(0);
return 0;
}
std::string
bunop::format() const
{
std::ostringstream out;
switch (op_)
{
case Star:
// Syntactic sugaring
if (min_ == 1 && max_ == unbounded)
{
out << "[+]";
return out.str();
}
out << "[*";
break;
}
if (min_ != 0 || max_ != unbounded)
{
// Always print the min_, even when it is equal to
// default_min, this way we avoid ambiguities (like
// when reading a[*..3];b[->..2] which actually
// means a[*0..3];b[->1..2].
out << min_;
if (min_ != max_)
{
out << "..";
if (max_ != unbounded)
out << max_;
}
}
out << ']';
return out.str();
}
bunop::map bunop::instances;
const formula*
bunop::instance(type op, const formula* child,
unsigned min, unsigned max)
{
assert(min <= max);
// Some trivial simplifications.
switch (op)
{
case Star:
{
// - [*0][*min..max] = [*0]
if (child == constant::empty_word_instance())
return child;
// - 0[*0..max] = [*0]
// - 0[*min..max] = 0 if min > 0
if (child == constant::false_instance())
{
if (min == 0)
return constant::empty_word_instance();
else
return child;
}
// - Exp[*0] = [*0]
if (max == 0)
{
child->destroy();
return constant::empty_word_instance();
}
// - Exp[*1] = Exp
if (min == 1 && max == 1)
return child;
// - Exp[*i..j][*min..max] = Exp[*i(min)..j(max)]
// if i*(min+1)<=j(min)+1.
if (const bunop* s = is_bunop(child))
{
unsigned i = s->min();
unsigned j = s->max();
// Exp has to be true between i*min and j*min
// then between i*(min+1) and j*(min+1)
// ...
// finally between i*max and j*max
//
// We can merge these intervals into [i*min..j*max] iff the
// first are adjacent or overlap, i.e. iff
// i*(min+1) <= j*min+1.
// (Because i<=j, this entails that the other intervals also
// overlap).
const formula* exp = s->child();
if (j == unbounded)
{
min *= i;
max = unbounded;
// Exp[*min..max]
exp->clone();
child->destroy();
child = exp;
}
else
{
if (i * (min + 1) <= (j * min) + 1)
{
min *= i;
if (max != unbounded)
{
if (j == unbounded)
max = unbounded;
else
max *= j;
}
exp->clone();
child->destroy();
child = exp;
}
}
}
break;
}
}
const formula* res;
auto ires = instances.insert(std::make_pair(key(op, child, min, max),
nullptr));
if (!ires.second)
{
// This instance already exists.
child->destroy();
res = ires.first->second;
}
else
{
res = ires.first->second = new bunop(op, child, min, max);
}
res->clone();
return res;
}
const formula*
bunop::sugar_goto(const formula* b, unsigned min, unsigned max)
{
assert(b->is_boolean());
// b[->min..max] is implemented as ((!b)[*];b)[*min..max]
const formula* s =
bunop::instance(bunop::Star,
unop::instance(unop::Not, b->clone()));
return bunop::instance(bunop::Star,
multop::instance(multop::Concat, s, b),
min, max);
}
const formula*
bunop::sugar_equal(const formula* b, unsigned min, unsigned max)
{
assert(b->is_boolean());
// b[=0..] = 1[*]
if (min == 0 && max == unbounded)
{
b->destroy();
return instance(Star, constant::true_instance());
}
// b[=min..max] is implemented as ((!b)[*];b)[*min..max];(!b)[*]
const formula* s =
bunop::instance(bunop::Star,
unop::instance(unop::Not, b->clone()));
const formula* t =
bunop::instance(bunop::Star,
multop::instance(multop::Concat,
s->clone(), b), min, max);
return multop::instance(multop::Concat, t, s);
}
unsigned
bunop::instance_count()
{
// Don't count one_star_ since it should not be destroyed.
return instances.size() - !!one_star_;
}
std::ostream&
bunop::dump_instances(std::ostream& os)
{
for (const auto& i: instances)
os << i.second << " = "
<< i.second->ref_count_() << " * "
<< i.second->dump()
<< std::endl;
return os;
}
}
}