mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
Use nullptr.
This commit is contained in:
parent
f01328c65f
commit
76eb7b9ede
625 changed files with 4639 additions and 4639 deletions
|
@ -146,7 +146,7 @@ namespace nlsat {
|
|||
m_todo(u),
|
||||
m_core1(s),
|
||||
m_core2(s),
|
||||
m_result(0),
|
||||
m_result(nullptr),
|
||||
m_evaluator(ev) {
|
||||
m_simplify_cores = false;
|
||||
m_full_dimensional = false;
|
||||
|
@ -521,7 +521,7 @@ namespace nlsat {
|
|||
|
||||
polynomial::var max_var(literal l) {
|
||||
atom * a = m_atoms[l.var()];
|
||||
if (a != 0)
|
||||
if (a != nullptr)
|
||||
return a->max_var();
|
||||
else
|
||||
return null_var;
|
||||
|
@ -535,7 +535,7 @@ namespace nlsat {
|
|||
for (unsigned i = 0; i < sz; i++) {
|
||||
literal l = ls[i];
|
||||
atom * a = m_atoms[l.var()];
|
||||
if (a != 0) {
|
||||
if (a != nullptr) {
|
||||
var x = a->max_var();
|
||||
SASSERT(x != null_var);
|
||||
if (max == null_var || x > max)
|
||||
|
@ -705,7 +705,7 @@ namespace nlsat {
|
|||
m_result = &result;
|
||||
add_root_literal(k, y, i, p);
|
||||
reset_already_added();
|
||||
m_result = 0;
|
||||
m_result = nullptr;
|
||||
}
|
||||
|
||||
void add_root_literal(atom::kind k, var y, unsigned i, poly * p) {
|
||||
|
@ -1232,7 +1232,7 @@ namespace nlsat {
|
|||
This method selects the equation of minimal degree in max.
|
||||
*/
|
||||
poly * select_eq(scoped_literal_vector & C, var max) {
|
||||
poly * r = 0;
|
||||
poly * r = nullptr;
|
||||
unsigned min_d = UINT_MAX;
|
||||
unsigned sz = C.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
|
@ -1289,7 +1289,7 @@ namespace nlsat {
|
|||
if (y >= max)
|
||||
continue;
|
||||
atom * eq = m_x2eq[y];
|
||||
if (eq == 0)
|
||||
if (eq == nullptr)
|
||||
continue;
|
||||
SASSERT(eq->is_ineq_atom());
|
||||
SASSERT(to_ineq_atom(eq)->size() == 1);
|
||||
|
@ -1305,7 +1305,7 @@ namespace nlsat {
|
|||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1315,7 +1315,7 @@ namespace nlsat {
|
|||
// Simplify using equations in the core
|
||||
while (!C.empty()) {
|
||||
poly * eq = select_eq(C, max);
|
||||
if (eq == 0)
|
||||
if (eq == nullptr)
|
||||
break;
|
||||
TRACE("nlsat_simplify_core", tout << "using equality for simplifying core\n";
|
||||
m_pm.display(tout, eq, m_solver.display_proc()); tout << "\n";);
|
||||
|
@ -1325,7 +1325,7 @@ namespace nlsat {
|
|||
// Simplify using equations using variables from lower stages.
|
||||
while (!C.empty()) {
|
||||
ineq_atom * eq = select_lower_stage_eq(C, max);
|
||||
if (eq == 0)
|
||||
if (eq == nullptr)
|
||||
break;
|
||||
SASSERT(eq->size() == 1);
|
||||
SASSERT(!eq->is_even(0));
|
||||
|
@ -1456,7 +1456,7 @@ namespace nlsat {
|
|||
m_result = &result;
|
||||
process(num, ls);
|
||||
reset_already_added();
|
||||
m_result = 0;
|
||||
m_result = nullptr;
|
||||
TRACE("nlsat_explain", tout << "[explain] result\n"; display(tout, result););
|
||||
CASSERT("nlsat", check_already_added());
|
||||
}
|
||||
|
@ -1495,14 +1495,14 @@ namespace nlsat {
|
|||
project(m_ps, mx_var);
|
||||
}
|
||||
reset_already_added();
|
||||
m_result = 0;
|
||||
m_result = nullptr;
|
||||
if (x != mx_var) {
|
||||
m_solver.restore_order();
|
||||
}
|
||||
}
|
||||
else {
|
||||
reset_already_added();
|
||||
m_result = 0;
|
||||
m_result = nullptr;
|
||||
}
|
||||
for (unsigned i = 0; i < result.size(); ++i) {
|
||||
result.set(i, ~result[i]);
|
||||
|
|
|
@ -122,7 +122,7 @@ namespace nlsat {
|
|||
}
|
||||
|
||||
void interval_set_manager::del(interval_set * s) {
|
||||
if (s == 0)
|
||||
if (s == nullptr)
|
||||
return;
|
||||
unsigned num = s->m_num_intervals;
|
||||
unsigned obj_sz = interval_set::get_obj_size(num);
|
||||
|
@ -270,9 +270,9 @@ namespace nlsat {
|
|||
|
||||
interval_set * interval_set_manager::mk_union(interval_set const * s1, interval_set const * s2) {
|
||||
TRACE("nlsat_interval", tout << "mk_union\ns1: "; display(tout, s1); tout << "\ns2: "; display(tout, s2); tout << "\n";);
|
||||
if (s1 == 0 || s1 == s2)
|
||||
if (s1 == nullptr || s1 == s2)
|
||||
return const_cast<interval_set*>(s2);
|
||||
if (s2 == 0)
|
||||
if (s2 == nullptr)
|
||||
return const_cast<interval_set*>(s1);
|
||||
if (s1->m_full)
|
||||
return const_cast<interval_set*>(s1);
|
||||
|
@ -514,22 +514,22 @@ namespace nlsat {
|
|||
}
|
||||
|
||||
bool interval_set_manager::is_full(interval_set const * s) {
|
||||
if (s == 0)
|
||||
if (s == nullptr)
|
||||
return false;
|
||||
return s->m_full == 1;
|
||||
}
|
||||
|
||||
unsigned interval_set_manager::num_intervals(interval_set const * s) const {
|
||||
if (s == 0) return 0;
|
||||
if (s == nullptr) return 0;
|
||||
return s->m_num_intervals;
|
||||
}
|
||||
|
||||
bool interval_set_manager::subset(interval_set const * s1, interval_set const * s2) {
|
||||
if (s1 == s2)
|
||||
return true;
|
||||
if (s1 == 0)
|
||||
if (s1 == nullptr)
|
||||
return true;
|
||||
if (s2 == 0)
|
||||
if (s2 == nullptr)
|
||||
return false;
|
||||
if (s2->m_full)
|
||||
return true;
|
||||
|
@ -616,7 +616,7 @@ namespace nlsat {
|
|||
}
|
||||
|
||||
bool interval_set_manager::set_eq(interval_set const * s1, interval_set const * s2) {
|
||||
if (s1 == 0 || s2 == 0)
|
||||
if (s1 == nullptr || s2 == nullptr)
|
||||
return s1 == s2;
|
||||
if (s1->m_full || s2->m_full)
|
||||
return s1->m_full == s2->m_full;
|
||||
|
@ -625,7 +625,7 @@ namespace nlsat {
|
|||
}
|
||||
|
||||
bool interval_set_manager::eq(interval_set const * s1, interval_set const * s2) {
|
||||
if (s1 == 0 || s2 == 0)
|
||||
if (s1 == nullptr || s2 == nullptr)
|
||||
return s1 == s2;
|
||||
if (s1->m_num_intervals != s2->m_num_intervals)
|
||||
return false;
|
||||
|
@ -674,7 +674,7 @@ namespace nlsat {
|
|||
|
||||
void interval_set_manager::peek_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize) {
|
||||
SASSERT(!is_full(s));
|
||||
if (s == 0) {
|
||||
if (s == nullptr) {
|
||||
if (randomize) {
|
||||
int num = m_rand() % 2 == 0 ? 1 : -1;
|
||||
#define MAX_RANDOM_DEN_K 4
|
||||
|
@ -744,7 +744,7 @@ namespace nlsat {
|
|||
}
|
||||
|
||||
void interval_set_manager::display(std::ostream & out, interval_set const * s) const {
|
||||
if (s == 0) {
|
||||
if (s == nullptr) {
|
||||
out << "{}";
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace nlsat {
|
|||
/**
|
||||
\brief Return the empty set.
|
||||
*/
|
||||
interval_set * mk_empty() { return 0; }
|
||||
interval_set * mk_empty() { return nullptr; }
|
||||
|
||||
/**
|
||||
\brief Return a set of composed of a single interval.
|
||||
|
@ -64,7 +64,7 @@ namespace nlsat {
|
|||
\brief Return true if s is the empty set.
|
||||
*/
|
||||
bool is_empty(interval_set const * s) {
|
||||
return s == 0;
|
||||
return s == nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -54,8 +54,8 @@ namespace nlsat {
|
|||
void * m_data;
|
||||
public:
|
||||
enum kind { NULL_JST = 0, DECISION, CLAUSE, LAZY };
|
||||
justification():m_data(TAG(void *, static_cast<void*>(0), NULL_JST)) { SASSERT(is_null()); }
|
||||
justification(bool):m_data(TAG(void *, static_cast<void*>(0), DECISION)) { SASSERT(is_decision()); }
|
||||
justification():m_data(TAG(void *, nullptr, NULL_JST)) { SASSERT(is_null()); }
|
||||
justification(bool):m_data(TAG(void *, nullptr, DECISION)) { SASSERT(is_decision()); }
|
||||
justification(clause * c):m_data(TAG(void *, c, CLAUSE)) { SASSERT(is_clause()); }
|
||||
justification(lazy_justification * j):m_data(TAG(void *, j, LAZY)) { SASSERT(is_lazy()); }
|
||||
kind get_kind() const { return static_cast<kind>(GET_TAG(m_data)); }
|
||||
|
|
|
@ -110,10 +110,10 @@ namespace nlsat {
|
|||
display_var_proc const * m_proc; // display external var ids
|
||||
perm_display_var_proc(var_vector & perm):
|
||||
m_perm(perm),
|
||||
m_proc(0) {
|
||||
m_proc(nullptr) {
|
||||
}
|
||||
std::ostream& operator()(std::ostream & out, var x) const override {
|
||||
if (m_proc == 0)
|
||||
if (m_proc == nullptr)
|
||||
m_default_display_var(out, x);
|
||||
else
|
||||
(*m_proc)(out, m_perm[x]);
|
||||
|
@ -197,7 +197,7 @@ namespace nlsat {
|
|||
bool_var b = mk_bool_var();
|
||||
SASSERT(b == true_bool_var);
|
||||
literal true_lit(b, false);
|
||||
mk_clause(1, &true_lit, false, 0);
|
||||
mk_clause(1, &true_lit, false, nullptr);
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & _p) {
|
||||
|
@ -259,11 +259,11 @@ namespace nlsat {
|
|||
void dec_ref(assumption) {}
|
||||
|
||||
void inc_ref(_assumption_set a) {
|
||||
if (a != 0) m_asm.inc_ref(a);
|
||||
if (a != nullptr) m_asm.inc_ref(a);
|
||||
}
|
||||
|
||||
void dec_ref(_assumption_set a) {
|
||||
if (a != 0) m_asm.dec_ref(a);
|
||||
if (a != nullptr) m_asm.dec_ref(a);
|
||||
}
|
||||
|
||||
void inc_ref(bool_var b) {
|
||||
|
@ -284,7 +284,7 @@ namespace nlsat {
|
|||
if (b == null_bool_var)
|
||||
return;
|
||||
atom * a = m_atoms[b];
|
||||
if (a == 0)
|
||||
if (a == nullptr)
|
||||
return;
|
||||
SASSERT(a->ref_count() > 0);
|
||||
a->dec_ref();
|
||||
|
@ -377,7 +377,7 @@ namespace nlsat {
|
|||
unsigned max = 0;
|
||||
for (literal l : c) {
|
||||
atom const * a = m_atoms[l.var()];
|
||||
if (a == 0)
|
||||
if (a == nullptr)
|
||||
continue;
|
||||
unsigned d = degree(a);
|
||||
if (d > max)
|
||||
|
@ -429,7 +429,7 @@ namespace nlsat {
|
|||
void vars(literal l, var_vector& vs) {
|
||||
vs.reset();
|
||||
atom * a = m_atoms[l.var()];
|
||||
if (a == 0) {
|
||||
if (a == nullptr) {
|
||||
|
||||
}
|
||||
else if (a->is_ineq_atom()) {
|
||||
|
@ -492,7 +492,7 @@ namespace nlsat {
|
|||
}
|
||||
|
||||
void del(atom * a) {
|
||||
if (a == 0)
|
||||
if (a == nullptr)
|
||||
return ;
|
||||
if (a->is_ineq_atom())
|
||||
del(to_ineq_atom(a));
|
||||
|
@ -664,11 +664,11 @@ namespace nlsat {
|
|||
bool operator()(literal l1, literal l2) const {
|
||||
atom * a1 = m.m_atoms[l1.var()];
|
||||
atom * a2 = m.m_atoms[l2.var()];
|
||||
if (a1 == 0 && a2 == 0)
|
||||
if (a1 == nullptr && a2 == nullptr)
|
||||
return l1.index() < l2.index();
|
||||
if (a1 == 0)
|
||||
if (a1 == nullptr)
|
||||
return true;
|
||||
if (a2 == 0)
|
||||
if (a2 == nullptr)
|
||||
return false;
|
||||
var x1 = a1->max_var();
|
||||
var x2 = a2->max_var();
|
||||
|
@ -712,8 +712,8 @@ namespace nlsat {
|
|||
|
||||
void mk_clause(unsigned num_lits, literal const * lits, assumption a) {
|
||||
SASSERT(num_lits > 0);
|
||||
_assumption_set as = 0;
|
||||
if (a != 0)
|
||||
_assumption_set as = nullptr;
|
||||
if (a != nullptr)
|
||||
as = m_asm.mk_leaf(a);
|
||||
mk_clause(num_lits, lits, false, as);
|
||||
}
|
||||
|
@ -931,7 +931,7 @@ namespace nlsat {
|
|||
}
|
||||
bool_var b = l.var();
|
||||
atom * a = m_atoms[b];
|
||||
if (a == 0) {
|
||||
if (a == nullptr) {
|
||||
return l_undef;
|
||||
}
|
||||
var max = a->max_var();
|
||||
|
@ -1037,7 +1037,7 @@ namespace nlsat {
|
|||
if (m_bvalues[b] != l_true)
|
||||
return;
|
||||
atom * a = m_atoms[b];
|
||||
if (a == 0 || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0))
|
||||
if (a == nullptr || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0))
|
||||
return;
|
||||
var x = m_xk;
|
||||
SASSERT(a->max_var() == x);
|
||||
|
@ -1082,13 +1082,13 @@ namespace nlsat {
|
|||
TRACE("nlsat_inf_set", tout << "infeasible set for literal: "; display(tout, l); tout << "\n"; m_ism.display(tout, curr_set); tout << "\n";);
|
||||
if (m_ism.is_empty(curr_set)) {
|
||||
TRACE("nlsat_inf_set", tout << "infeasible set is empty, found literal\n";);
|
||||
R_propagate(l, 0);
|
||||
R_propagate(l, nullptr);
|
||||
SASSERT(is_satisfied(cls));
|
||||
return true;
|
||||
}
|
||||
if (m_ism.is_full(curr_set)) {
|
||||
TRACE("nlsat_inf_set", tout << "infeasible set is R, skip literal\n";);
|
||||
R_propagate(~l, 0);
|
||||
R_propagate(~l, nullptr);
|
||||
continue;
|
||||
}
|
||||
if (m_ism.subset(curr_set, xk_set)) {
|
||||
|
@ -1155,7 +1155,7 @@ namespace nlsat {
|
|||
if (!process_clause(*c, false))
|
||||
return c;
|
||||
}
|
||||
return 0; // succeeded
|
||||
return nullptr; // succeeded
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1235,7 +1235,7 @@ namespace nlsat {
|
|||
conflict_clause = process_clauses(m_bwatches[m_bk]);
|
||||
else
|
||||
conflict_clause = process_clauses(m_watches[m_xk]);
|
||||
if (conflict_clause == 0)
|
||||
if (conflict_clause == nullptr)
|
||||
break;
|
||||
if (!resolve(*conflict_clause))
|
||||
return l_false;
|
||||
|
@ -1299,7 +1299,7 @@ namespace nlsat {
|
|||
m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p2, &is_even));
|
||||
|
||||
// perform branch and bound
|
||||
clause * cls = mk_clause(m_lemma.size(), m_lemma.c_ptr(), false, 0);
|
||||
clause * cls = mk_clause(m_lemma.size(), m_lemma.c_ptr(), false, nullptr);
|
||||
if (cls) {
|
||||
TRACE("nlsat", display(tout << "conflict " << lo << " " << hi, *cls); tout << "\n";);
|
||||
}
|
||||
|
@ -1403,7 +1403,7 @@ namespace nlsat {
|
|||
unsigned sz = assumptions.size();
|
||||
literal const* ptr = assumptions.c_ptr();
|
||||
_assumption_set asms = static_cast<_assumption_set>(c.assumptions());
|
||||
if (asms == 0) {
|
||||
if (asms == nullptr) {
|
||||
return false;
|
||||
}
|
||||
vector<assumption, false> deps;
|
||||
|
@ -1663,7 +1663,7 @@ namespace nlsat {
|
|||
|
||||
m_num_marks = 0;
|
||||
m_lemma.reset();
|
||||
m_lemma_assumptions = 0;
|
||||
m_lemma_assumptions = nullptr;
|
||||
|
||||
resolve_clause(null_bool_var, *conflict_clause);
|
||||
|
||||
|
@ -1757,7 +1757,7 @@ namespace nlsat {
|
|||
// in a previous scope level. We may backjump many decisions.
|
||||
//
|
||||
unsigned sz = m_lemma.size();
|
||||
clause * new_cls = 0;
|
||||
clause * new_cls = nullptr;
|
||||
if (!found_decision) {
|
||||
// Case 1)
|
||||
// We just have to find the maximal variable in m_lemma, and return to that stage
|
||||
|
@ -1924,7 +1924,7 @@ namespace nlsat {
|
|||
void collect(literal l) {
|
||||
bool_var b = l.var();
|
||||
atom * a = m_atoms[b];
|
||||
if (a == 0)
|
||||
if (a == nullptr)
|
||||
return;
|
||||
if (a->is_ineq_atom()) {
|
||||
unsigned sz = to_ineq_atom(a)->size();
|
||||
|
@ -2123,7 +2123,7 @@ namespace nlsat {
|
|||
for (unsigned i = 0; i < sz; i++) {
|
||||
bool_var b = c[i].var();
|
||||
atom * a = m_atoms[b];
|
||||
if (a == 0)
|
||||
if (a == nullptr)
|
||||
continue;
|
||||
if (a->is_ineq_atom())
|
||||
continue;
|
||||
|
@ -2158,7 +2158,7 @@ namespace nlsat {
|
|||
reinit_cache(m_atoms[b]);
|
||||
}
|
||||
void reinit_cache(atom* a) {
|
||||
if (a == 0) {
|
||||
if (a == nullptr) {
|
||||
|
||||
}
|
||||
else if (a->is_ineq_atom()) {
|
||||
|
@ -2250,7 +2250,7 @@ namespace nlsat {
|
|||
|
||||
bool is_full_dimensional(literal l) const {
|
||||
atom * a = m_atoms[l.var()];
|
||||
if (a == 0)
|
||||
if (a == nullptr)
|
||||
return true;
|
||||
switch (a->get_kind()) {
|
||||
case atom::EQ: return l.sign();
|
||||
|
@ -2858,7 +2858,7 @@ namespace nlsat {
|
|||
}
|
||||
|
||||
std::ostream& display(std::ostream & out, clause const & c, display_var_proc const & proc) const {
|
||||
if (c.assumptions() != 0) {
|
||||
if (c.assumptions() != nullptr) {
|
||||
display_assumptions(out, static_cast<_assumption_set>(c.assumptions()));
|
||||
out << " |- ";
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ namespace nlsat {
|
|||
/**
|
||||
\brief Create a new clause.
|
||||
*/
|
||||
void mk_clause(unsigned num_lits, literal * lits, assumption a = 0);
|
||||
void mk_clause(unsigned num_lits, literal * lits, assumption a = nullptr);
|
||||
|
||||
// -----------------------
|
||||
//
|
||||
|
|
|
@ -269,12 +269,12 @@ struct goal2nlsat::scoped_set_imp {
|
|||
}
|
||||
|
||||
~scoped_set_imp() {
|
||||
m_owner.m_imp = 0;
|
||||
m_owner.m_imp = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
goal2nlsat::goal2nlsat() {
|
||||
m_imp = 0;
|
||||
m_imp = nullptr;
|
||||
}
|
||||
|
||||
goal2nlsat::~goal2nlsat() {
|
||||
|
|
|
@ -68,7 +68,7 @@ class nlsat_tactic : public tactic {
|
|||
}
|
||||
for (unsigned b = 0; b < b2a.size(); b++) {
|
||||
expr * a = b2a.get(b);
|
||||
if (a == 0)
|
||||
if (a == nullptr)
|
||||
continue;
|
||||
if (is_uninterp_const(a))
|
||||
continue;
|
||||
|
@ -116,7 +116,7 @@ class nlsat_tactic : public tactic {
|
|||
}
|
||||
for (unsigned b = 0; b < b2a.size(); b++) {
|
||||
expr * a = b2a.get(b);
|
||||
if (a == 0 || !is_uninterp_const(a))
|
||||
if (a == nullptr || !is_uninterp_const(a))
|
||||
continue;
|
||||
lbool val = m_solver.bvalue(b);
|
||||
if (val == l_undef)
|
||||
|
@ -134,7 +134,7 @@ class nlsat_tactic : public tactic {
|
|||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
mc = 0; pc = 0; core = 0;
|
||||
mc = nullptr; pc = nullptr; core = nullptr;
|
||||
tactic_report report("nlsat", *g);
|
||||
|
||||
if (g->is_decided()) {
|
||||
|
@ -173,7 +173,7 @@ class nlsat_tactic : public tactic {
|
|||
}
|
||||
}
|
||||
else {
|
||||
expr_dependency* lcore = 0;
|
||||
expr_dependency* lcore = nullptr;
|
||||
if (g->unsat_core_enabled()) {
|
||||
vector<nlsat::assumption, false> assumptions;
|
||||
m_solver.get_core(assumptions);
|
||||
|
@ -182,7 +182,7 @@ class nlsat_tactic : public tactic {
|
|||
lcore = m.mk_join(lcore, d);
|
||||
}
|
||||
}
|
||||
g->assert_expr(m.mk_false(), 0, lcore);
|
||||
g->assert_expr(m.mk_false(), nullptr, lcore);
|
||||
}
|
||||
|
||||
g->inc_depth();
|
||||
|
@ -204,14 +204,14 @@ class nlsat_tactic : public tactic {
|
|||
|
||||
~scoped_set_imp() {
|
||||
m_owner.m_imp->m_solver.collect_statistics(m_owner.m_stats);
|
||||
m_owner.m_imp = 0;
|
||||
m_owner.m_imp = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
nlsat_tactic(params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = 0;
|
||||
m_imp = nullptr;
|
||||
}
|
||||
|
||||
tactic * translate(ast_manager & m) override {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue