mirror of
https://github.com/Z3Prover/z3
synced 2025-06-07 14:43:23 +00:00
separate out file for objectives
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
759d80dfe3
commit
401fced400
10 changed files with 199 additions and 61 deletions
|
@ -133,6 +133,8 @@ namespace opt {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TBD: bug when cancel flag is set, fu_malik returns is_sat == l_true instead of l_undef
|
||||||
|
|
||||||
lbool fu_malik_maxsat(solver& s, expr_ref_vector& soft_constraints) {
|
lbool fu_malik_maxsat(solver& s, expr_ref_vector& soft_constraints) {
|
||||||
ast_manager& m = soft_constraints.get_manager();
|
ast_manager& m = soft_constraints.get_manager();
|
||||||
lbool is_sat = s.check_sat(0,0);
|
lbool is_sat = s.check_sat(0,0);
|
||||||
|
|
|
@ -21,8 +21,6 @@ Notes:
|
||||||
--*/
|
--*/
|
||||||
|
|
||||||
#include "opt_context.h"
|
#include "opt_context.h"
|
||||||
#include "fu_malik.h"
|
|
||||||
#include "weighted_maxsat.h"
|
|
||||||
#include "ast_pp.h"
|
#include "ast_pp.h"
|
||||||
#include "opt_solver.h"
|
#include "opt_solver.h"
|
||||||
#include "arith_decl_plugin.h"
|
#include "arith_decl_plugin.h"
|
||||||
|
@ -37,7 +35,8 @@ namespace opt {
|
||||||
m_hard_constraints(m),
|
m_hard_constraints(m),
|
||||||
m_soft_constraints(m),
|
m_soft_constraints(m),
|
||||||
m_objectives(m),
|
m_objectives(m),
|
||||||
m_opt_objectives(m)
|
m_opt_objectives(m),
|
||||||
|
m_maxsmt(m)
|
||||||
{
|
{
|
||||||
m_params.set_bool("model", true);
|
m_params.set_bool("model", true);
|
||||||
m_params.set_bool("unsat_core", true);
|
m_params.set_bool("unsat_core", true);
|
||||||
|
@ -45,56 +44,29 @@ namespace opt {
|
||||||
|
|
||||||
void context::optimize() {
|
void context::optimize() {
|
||||||
|
|
||||||
expr_ref_vector const& fmls = m_soft_constraints;
|
|
||||||
|
|
||||||
if (!m_solver) {
|
if (!m_solver) {
|
||||||
symbol logic;
|
symbol logic;
|
||||||
set_solver(alloc(opt_solver, m, m_params, logic));
|
set_solver(alloc(opt_solver, m, m_params, logic));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// really just works for opt_solver now.
|
||||||
solver* s = m_solver.get();
|
solver* s = m_solver.get();
|
||||||
|
opt_solver::scoped_push _sp(get_opt_solver(*s));
|
||||||
|
|
||||||
for (unsigned i = 0; i < m_hard_constraints.size(); ++i) {
|
for (unsigned i = 0; i < m_hard_constraints.size(); ++i) {
|
||||||
s->assert_expr(m_hard_constraints[i].get());
|
s->assert_expr(m_hard_constraints[i].get());
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_ref_vector fmls_copy(fmls);
|
|
||||||
lbool is_sat;
|
lbool is_sat;
|
||||||
if (!fmls.empty()) {
|
|
||||||
// TBD: bug when cancel flag is set, fu_malik returns is_sat == l_true instead of l_undef
|
is_sat = m_maxsmt(get_opt_solver(*s), m_soft_constraints, m_weights);
|
||||||
if (is_maxsat_problem()) {
|
|
||||||
is_sat = opt::fu_malik_maxsat(*s, fmls_copy);
|
for (unsigned i = 0; i < m_soft_constraints.size(); ++i) {
|
||||||
}
|
s->assert_expr(m_soft_constraints[i].get());
|
||||||
else {
|
|
||||||
is_sat = weighted_maxsat(get_opt_solver(*s), fmls_copy, m_weights);
|
|
||||||
}
|
|
||||||
std::cout << "is-sat: " << is_sat << "\n";
|
|
||||||
if (is_sat != l_true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::cout << "Satisfying soft constraints\n";
|
|
||||||
for (unsigned i = 0; i < fmls_copy.size(); ++i) {
|
|
||||||
std::cout << mk_pp(fmls_copy[i].get(), m) << "\n";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_objectives.empty()) {
|
if (is_sat == l_true) {
|
||||||
vector<inf_eps_rational<inf_rational> > values;
|
is_sat = m_opt_objectives(get_opt_solver(*s), m_objectives);
|
||||||
for (unsigned i = 0; i < fmls_copy.size(); ++i) {
|
|
||||||
s->assert_expr(fmls_copy[i].get());
|
|
||||||
}
|
|
||||||
is_sat = m_opt_objectives(get_opt_solver(*s), m_objectives, values);
|
|
||||||
std::cout << "is-sat: " << is_sat << std::endl;
|
|
||||||
|
|
||||||
if (is_sat != l_true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < values.size(); ++i) {
|
|
||||||
if (!m_is_max[i]) {
|
|
||||||
values[i].neg();
|
|
||||||
}
|
|
||||||
std::cout << "objective value: " << mk_pp(m_objectives[i].get(), m) << " -> " << values[i].to_string() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_objectives.empty() && m_soft_constraints.empty()) {
|
if (m_objectives.empty() && m_soft_constraints.empty()) {
|
||||||
|
@ -103,15 +75,6 @@ namespace opt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool context::is_maxsat_problem() const {
|
|
||||||
vector<rational> const& ws = m_weights;
|
|
||||||
for (unsigned i = 0; i < ws.size(); ++i) {
|
|
||||||
if (!ws[i].is_one()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
opt_solver& context::get_opt_solver(solver& s) {
|
opt_solver& context::get_opt_solver(solver& s) {
|
||||||
if (typeid(opt_solver) != typeid(s)) {
|
if (typeid(opt_solver) != typeid(s)) {
|
||||||
|
@ -125,6 +88,7 @@ namespace opt {
|
||||||
m_solver->cancel();
|
m_solver->cancel();
|
||||||
}
|
}
|
||||||
m_opt_objectives.set_cancel(true);
|
m_opt_objectives.set_cancel(true);
|
||||||
|
m_maxsmt.set_cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void context::reset_cancel() {
|
void context::reset_cancel() {
|
||||||
|
@ -132,6 +96,7 @@ namespace opt {
|
||||||
m_solver->reset_cancel();
|
m_solver->reset_cancel();
|
||||||
}
|
}
|
||||||
m_opt_objectives.set_cancel(false);
|
m_opt_objectives.set_cancel(false);
|
||||||
|
m_maxsmt.set_cancel(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void context::add_objective(app* t, bool is_max) {
|
void context::add_objective(app* t, bool is_max) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ Notes:
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "solver.h"
|
#include "solver.h"
|
||||||
#include "optimize_objectives.h"
|
#include "optimize_objectives.h"
|
||||||
|
#include "opt_maxsmt.h"
|
||||||
|
|
||||||
namespace opt {
|
namespace opt {
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ namespace opt {
|
||||||
ref<solver> m_solver;
|
ref<solver> m_solver;
|
||||||
params_ref m_params;
|
params_ref m_params;
|
||||||
optimize_objectives m_opt_objectives;
|
optimize_objectives m_opt_objectives;
|
||||||
|
maxsmt m_maxsmt;
|
||||||
public:
|
public:
|
||||||
context(ast_manager& m);
|
context(ast_manager& m);
|
||||||
|
|
||||||
|
|
66
src/opt/opt_maxsmt.cpp
Normal file
66
src/opt/opt_maxsmt.cpp
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2013 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
opt_maxsmt.cpp
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
MaxSMT optimization context.
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Nikolaj Bjorner (nbjorner) 2013-11-7
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
--*/
|
||||||
|
|
||||||
|
#include "opt_maxsmt.h"
|
||||||
|
#include "fu_malik.h"
|
||||||
|
#include "weighted_maxsat.h"
|
||||||
|
#include "ast_pp.h"
|
||||||
|
|
||||||
|
namespace opt {
|
||||||
|
|
||||||
|
lbool maxsmt::operator()(opt_solver& s, expr_ref_vector& fmls, vector<rational> const& ws) {
|
||||||
|
lbool is_sat;
|
||||||
|
|
||||||
|
if (fmls.empty()) {
|
||||||
|
is_sat = s.check_sat(0, 0);
|
||||||
|
}
|
||||||
|
else if (is_maxsat_problem(ws)) {
|
||||||
|
is_sat = opt::fu_malik_maxsat(s, fmls);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
is_sat = weighted_maxsat(s, fmls, ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infrastructure for displaying and storing solution is TBD.
|
||||||
|
std::cout << "is-sat: " << is_sat << "\n";
|
||||||
|
if (is_sat == l_true) {
|
||||||
|
std::cout << "Satisfying soft constraints\n";
|
||||||
|
for (unsigned i = 0; i < fmls.size(); ++i) {
|
||||||
|
std::cout << mk_pp(fmls[i].get(), m) << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return is_sat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void maxsmt::set_cancel(bool f) {
|
||||||
|
m_cancel = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool maxsmt::is_maxsat_problem(vector<rational> const& ws) const {
|
||||||
|
for (unsigned i = 0; i < ws.size(); ++i) {
|
||||||
|
if (!ws[i].is_one()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
52
src/opt/opt_maxsmt.h
Normal file
52
src/opt/opt_maxsmt.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2013 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
opt_maxsmt.h
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
MaxSMT optimization context.
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Nikolaj Bjorner (nbjorner) 2013-11-7
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#ifndef _OPT_MAXSMT_H_
|
||||||
|
#define _OPT_MAXSMT_H_
|
||||||
|
|
||||||
|
#include "opt_solver.h"
|
||||||
|
|
||||||
|
namespace opt {
|
||||||
|
/**
|
||||||
|
Takes solver with hard constraints added.
|
||||||
|
Returns modified soft constraints that are maximal assignments.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class maxsmt {
|
||||||
|
ast_manager& m;
|
||||||
|
opt_solver* s;
|
||||||
|
volatile bool m_cancel;
|
||||||
|
symbol m_engine;
|
||||||
|
public:
|
||||||
|
maxsmt(ast_manager& m): m(m), s(0), m_cancel(false) {}
|
||||||
|
|
||||||
|
lbool operator()(opt_solver& s, expr_ref_vector& soft, vector<rational> const& weights);
|
||||||
|
|
||||||
|
void set_cancel(bool f);
|
||||||
|
|
||||||
|
void set_engine(symbol const& e) { m_engine = e; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool is_maxsat_problem(vector<rational> const& ws) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -204,23 +204,25 @@ namespace opt {
|
||||||
Takes solver with hard constraints added.
|
Takes solver with hard constraints added.
|
||||||
Returns an optimal assignment to objective functions.
|
Returns an optimal assignment to objective functions.
|
||||||
*/
|
*/
|
||||||
lbool optimize_objectives::operator()(opt_solver& solver, app_ref_vector& objectives, vector<inf_eps>& values) {
|
lbool optimize_objectives::operator()(opt_solver& solver, app_ref_vector const& objectives) {
|
||||||
s = &solver;
|
s = &solver;
|
||||||
s->reset_objectives();
|
s->reset_objectives();
|
||||||
m_lower.reset();
|
m_lower.reset();
|
||||||
m_upper.reset();
|
m_upper.reset();
|
||||||
|
m_objs.reset();
|
||||||
for (unsigned i = 0; i < objectives.size(); ++i) {
|
for (unsigned i = 0; i < objectives.size(); ++i) {
|
||||||
m_lower.push_back(inf_eps(rational(-1),inf_rational(0)));
|
m_lower.push_back(inf_eps(rational(-1),inf_rational(0)));
|
||||||
m_upper.push_back(inf_eps(rational(1), inf_rational(0)));
|
m_upper.push_back(inf_eps(rational(1), inf_rational(0)));
|
||||||
|
m_objs.push_back(objectives[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First check_sat call to initialize theories
|
// First check_sat call to initialize theories
|
||||||
lbool is_sat = s->check_sat(0, 0);
|
lbool is_sat = s->check_sat(0, 0);
|
||||||
if (is_sat == l_true) {
|
if (is_sat == l_true && !objectives.empty()) {
|
||||||
opt_solver::scoped_push _push(*s);
|
opt_solver::scoped_push _push(*s);
|
||||||
|
|
||||||
for (unsigned i = 0; i < objectives.size(); ++i) {
|
for (unsigned i = 0; i < objectives.size(); ++i) {
|
||||||
m_vars.push_back(s->add_objective(objectives[i].get()));
|
m_vars.push_back(s->add_objective(objectives[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_engine == symbol("basic")) {
|
if (m_engine == symbol("basic")) {
|
||||||
|
@ -235,12 +237,31 @@ namespace opt {
|
||||||
NOT_IMPLEMENTED_YET();
|
NOT_IMPLEMENTED_YET();
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
values.reset();
|
|
||||||
values.append(m_lower);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "is-sat: " << is_sat << std::endl;
|
||||||
|
display(std::cout);
|
||||||
|
|
||||||
return is_sat;
|
return is_sat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inf_eps optimize_objectives::get_value(bool as_positive, unsigned index) const {
|
||||||
|
if (as_positive) {
|
||||||
|
return m_lower[index];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return -m_lower[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void optimize_objectives::display(std::ostream& out) const {
|
||||||
|
unsigned sz = m_objs.size();
|
||||||
|
for (unsigned i = 0; i < sz; ++i) {
|
||||||
|
out << "objective value: " << mk_pp(m_objs[i], m) << " -> " << get_value(true, i).to_string() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -33,17 +33,22 @@ namespace opt {
|
||||||
volatile bool m_cancel;
|
volatile bool m_cancel;
|
||||||
vector<inf_eps> m_lower;
|
vector<inf_eps> m_lower;
|
||||||
vector<inf_eps> m_upper;
|
vector<inf_eps> m_upper;
|
||||||
|
app_ref_vector m_objs;
|
||||||
svector<smt::theory_var> m_vars;
|
svector<smt::theory_var> m_vars;
|
||||||
symbol m_engine;
|
symbol m_engine;
|
||||||
public:
|
public:
|
||||||
optimize_objectives(ast_manager& m): m(m), s(0), m_cancel(false) {}
|
optimize_objectives(ast_manager& m): m(m), s(0), m_cancel(false), m_objs(m) {}
|
||||||
|
|
||||||
lbool operator()(opt_solver& s, app_ref_vector& objectives, vector<inf_eps>& values);
|
lbool operator()(opt_solver& s, app_ref_vector const& objectives);
|
||||||
|
|
||||||
void set_cancel(bool f);
|
void set_cancel(bool f);
|
||||||
|
|
||||||
void set_engine(symbol const& e) { m_engine = e; }
|
void set_engine(symbol const& e) { m_engine = e; }
|
||||||
|
|
||||||
|
void display(std::ostream& out) const;
|
||||||
|
|
||||||
|
inf_eps get_value(bool as_positive, unsigned index) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
lbool basic_opt();
|
lbool basic_opt();
|
||||||
|
|
|
@ -66,10 +66,13 @@ namespace smt {
|
||||||
SASSERT(m_util.is_at_most_k(atom));
|
SASSERT(m_util.is_at_most_k(atom));
|
||||||
unsigned k = m_util.get_k(atom);
|
unsigned k = m_util.get_k(atom);
|
||||||
|
|
||||||
|
|
||||||
if (ctx.b_internalized(atom)) {
|
if (ctx.b_internalized(atom)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_stats.m_num_predicates++;
|
||||||
|
|
||||||
TRACE("card", tout << "internalize: " << mk_pp(atom, m) << "\n";);
|
TRACE("card", tout << "internalize: " << mk_pp(atom, m) << "\n";);
|
||||||
|
|
||||||
SASSERT(!ctx.b_internalized(atom));
|
SASSERT(!ctx.b_internalized(atom));
|
||||||
|
@ -210,7 +213,10 @@ namespace smt {
|
||||||
m_cards_trail.push_back(abv);
|
m_cards_trail.push_back(abv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void theory_card::collect_statistics(::statistics& st) const {
|
||||||
|
st.update("pb axioms", m_stats.m_num_axioms);
|
||||||
|
st.update("pb predicates", m_stats.m_num_predicates);
|
||||||
|
}
|
||||||
|
|
||||||
void theory_card::reset_eh() {
|
void theory_card::reset_eh() {
|
||||||
|
|
||||||
|
@ -229,6 +235,7 @@ namespace smt {
|
||||||
m_cards_lim.reset();
|
m_cards_lim.reset();
|
||||||
m_watch_trail.reset();
|
m_watch_trail.reset();
|
||||||
m_watch_lim.reset();
|
m_watch_lim.reset();
|
||||||
|
m_stats.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void theory_card::update_min_max(bool_var v, bool is_true, card* c) {
|
void theory_card::update_min_max(bool_var v, bool is_true, card* c) {
|
||||||
|
@ -470,8 +477,16 @@ namespace smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
void theory_card::add_clause(literal_vector const& lits) {
|
void theory_card::add_clause(literal_vector const& lits) {
|
||||||
|
m_stats.m_num_axioms++;
|
||||||
context& ctx = get_context();
|
context& ctx = get_context();
|
||||||
TRACE("card", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";);
|
TRACE("card", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";);
|
||||||
ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
|
justification* js = 0;
|
||||||
|
ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0);
|
||||||
|
IF_VERBOSE(0,
|
||||||
|
for (unsigned i = 0; i < lits.size(); ++i) {
|
||||||
|
verbose_stream() << lits[i] << " ";
|
||||||
|
}
|
||||||
|
verbose_stream() << "\n";);
|
||||||
|
// ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,13 @@ namespace smt {
|
||||||
|
|
||||||
typedef svector<std::pair<bool_var, int> > arg_t;
|
typedef svector<std::pair<bool_var, int> > arg_t;
|
||||||
|
|
||||||
|
struct stats {
|
||||||
|
unsigned m_num_axioms;
|
||||||
|
unsigned m_num_predicates;
|
||||||
|
void reset() { memset(this, 0, sizeof(*this)); }
|
||||||
|
stats() { reset(); }
|
||||||
|
};
|
||||||
|
|
||||||
struct card {
|
struct card {
|
||||||
int m_k;
|
int m_k;
|
||||||
bool_var m_bv;
|
bool_var m_bv;
|
||||||
|
@ -49,6 +56,7 @@ namespace smt {
|
||||||
unsigned_vector m_watch_lim;
|
unsigned_vector m_watch_lim;
|
||||||
literal_vector m_literals;
|
literal_vector m_literals;
|
||||||
card_util m_util;
|
card_util m_util;
|
||||||
|
stats m_stats;
|
||||||
|
|
||||||
void add_watch(bool_var bv, card* c);
|
void add_watch(bool_var bv, card* c);
|
||||||
void add_card(card* c);
|
void add_card(card* c);
|
||||||
|
@ -84,6 +92,7 @@ namespace smt {
|
||||||
virtual void init_search_eh();
|
virtual void init_search_eh();
|
||||||
virtual void push_scope_eh();
|
virtual void push_scope_eh();
|
||||||
virtual void pop_scope_eh(unsigned num_scopes);
|
virtual void pop_scope_eh(unsigned num_scopes);
|
||||||
|
virtual void collect_statistics(::statistics & st) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -92,6 +92,7 @@ class lia2card_tactic : public tactic {
|
||||||
it = m_01s.begin(), end = m_01s.end();
|
it = m_01s.begin(), end = m_01s.end();
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
if (!validate_uses(m_uses.find(*it))) {
|
if (!validate_uses(m_uses.find(*it))) {
|
||||||
|
std::cout << "did not validate: " << mk_pp(*it, m) << "\n";
|
||||||
m_uses.remove(*it);
|
m_uses.remove(*it);
|
||||||
m_01s.remove(*it);
|
m_01s.remove(*it);
|
||||||
it = m_01s.begin();
|
it = m_01s.begin();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue