/****************************************************************************** * Top contributors (to current version): * Liana Hadarean, Dejan Jovanovic, Mathias Preiner * * This file is part of the cvc5 project. * * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS * in the top-level source directory and their institutional affiliations. * All rights reserved. See the file COPYING in the top-level source * directory for licensing information. * **************************************************************************** * * Various utility functions for bit-blasting. */ #include "cvc5_private.h" #ifndef CVC5__THEORY__BV__BITBLAST__BITBLAST_UTILS_H #define CVC5__THEORY__BV__BITBLAST__BITBLAST_UTILS_H #include #include "expr/node.h" namespace cvc5 { namespace theory { namespace bv { template class TBitblaster; template std::string toString (const std::vector& bits); template <> inline std::string toString (const std::vector& bits) { std::ostringstream os; for (int i = bits.size() - 1; i >= 0; --i) { TNode bit = bits[i]; if (bit.getKind() == kind::CONST_BOOLEAN) { os << (bit.getConst() ? "1" : "0"); } else { os << bit<< " "; } } os <<"\n"; return os.str(); } template T mkTrue(); template T mkFalse(); template T mkNot(T a); template T mkOr(T a, T b); template T mkOr(const std::vector& a); template T mkAnd(T a, T b); template T mkAnd(const std::vector& a); template T mkXor(T a, T b); template T mkIff(T a, T b); template T mkIte(T cond, T a, T b); template <> inline Node mkTrue() { return NodeManager::currentNM()->mkConst(true); } template <> inline Node mkFalse() { return NodeManager::currentNM()->mkConst(false); } template <> inline Node mkNot(Node a) { return NodeManager::currentNM()->mkNode(kind::NOT, a); } template <> inline Node mkOr(Node a, Node b) { return NodeManager::currentNM()->mkNode(kind::OR, a, b); } template <> inline Node mkOr(const std::vector& children) { Assert(children.size()); if (children.size() == 1) return children[0]; return NodeManager::currentNM()->mkNode(kind::OR, children); } template <> inline Node mkAnd(Node a, Node b) { return NodeManager::currentNM()->mkNode(kind::AND, a, b); } template <> inline Node mkAnd(const std::vector& children) { Assert(children.size()); if (children.size() == 1) return children[0]; return NodeManager::currentNM()->mkNode(kind::AND, children); } template <> inline Node mkXor(Node a, Node b) { return NodeManager::currentNM()->mkNode(kind::XOR, a, b); } template <> inline Node mkIff(Node a, Node b) { return NodeManager::currentNM()->mkNode(kind::EQUAL, a, b); } template <> inline Node mkIte(Node cond, Node a, Node b) { return NodeManager::currentNM()->mkNode(kind::ITE, cond, a, b); } /* Various helper functions that get called by the bitblasting procedures */ template void inline extractBits(const std::vector& b, std::vector& dest, unsigned lo, unsigned hi) { Assert(lo < b.size() && hi < b.size() && lo <= hi); for (unsigned i = lo; i <= hi; ++i) { dest.push_back(b[i]); } } template void inline negateBits(const std::vector& bits, std::vector& negated_bits) { for(unsigned i = 0; i < bits.size(); ++i) { negated_bits.push_back(mkNot(bits[i])); } } template bool inline isZero(const std::vector& bits) { for(unsigned i = 0; i < bits.size(); ++i) { if(bits[i] != mkFalse()) { return false; } } return true; } template void inline rshift(std::vector& bits, unsigned amount) { for (unsigned i = 0; i < bits.size() - amount; ++i) { bits[i] = bits[i+amount]; } for(unsigned i = bits.size() - amount; i < bits.size(); ++i) { bits[i] = mkFalse(); } } template void inline lshift(std::vector& bits, unsigned amount) { for (int i = (int)bits.size() - 1; i >= (int)amount ; --i) { bits[i] = bits[i-amount]; } for(unsigned i = 0; i < amount; ++i) { bits[i] = mkFalse(); } } template void inline makeZero(std::vector& bits, unsigned width) { Assert(bits.size() == 0); for(unsigned i = 0; i < width; ++i) { bits.push_back(mkFalse()); } } /** * Constructs a simple ripple carry adder * * @param a first term to be added * @param b second term to be added * @param res the result * @param carry the carry-in bit * * @return the carry-out */ template T inline rippleCarryAdder(const std::vector&a, const std::vector& b, std::vector& res, T carry) { Assert(a.size() == b.size() && res.size() == 0); for (unsigned i = 0 ; i < a.size(); ++i) { T sum = mkXor(mkXor(a[i], b[i]), carry); carry = mkOr( mkAnd(a[i], b[i]), mkAnd( mkXor(a[i], b[i]), carry)); res.push_back(sum); } return carry; } template inline void shiftAddMultiplier(const std::vector&a, const std::vector&b, std::vector& res) { for (unsigned i = 0; i < a.size(); ++i) { res.push_back(mkAnd(b[0], a[i])); } for(unsigned k = 1; k < res.size(); ++k) { T carry_in = mkFalse(); T carry_out; for(unsigned j = 0; j < res.size() -k; ++j) { T aj = mkAnd(b[k], a[j]); carry_out = mkOr(mkAnd(res[j+k], aj), mkAnd( mkXor(res[j+k], aj), carry_in)); res[j+k] = mkXor(mkXor(res[j+k], aj), carry_in); carry_in = carry_out; } } } template T inline uLessThanBB(const std::vector&a, const std::vector& b, bool orEqual) { Assert(a.size() && b.size()); T res = mkAnd(mkNot(a[0]), b[0]); if(orEqual) { res = mkOr(res, mkIff(a[0], b[0])); } for (unsigned i = 1; i < a.size(); ++i) { // a < b iff ( a[i] <-> b[i] AND a[i-1:0] < b[i-1:0]) OR (~a[i] AND b[i]) res = mkOr(mkAnd(mkIff(a[i], b[i]), res), mkAnd(mkNot(a[i]), b[i])); } return res; } template T inline sLessThanBB(const std::vector&a, const std::vector& b, bool orEqual) { Assert(a.size() && b.size()); if (a.size() == 1) { if(orEqual) { return mkOr(mkIff(a[0], b[0]), mkAnd(a[0], mkNot(b[0]))); } return mkAnd(a[0], mkNot(b[0])); } unsigned n = a.size() - 1; std::vector a1, b1; extractBits(a, a1, 0, n-1); extractBits(b, b1, 0, n-1); // unsigned comparison of the first n-1 bits T ures = uLessThanBB(a1, b1, orEqual); T res = mkOr(// a b have the same sign mkAnd(mkIff(a[n], b[n]), ures), // a is negative and b positive mkAnd(a[n], mkNot(b[n]))); return res; } } // namespace bv } // namespace theory } // namespace cvc5 #endif // CVC5__THEORY__BV__BITBLAST__BITBLAST_UTILS_H