Add `isNull` and `isUpdater` to `Sort` class of python API (#7423)
[cvc5.git] / src / api / python / cvc5.pxi
index a512a17a8521586db31e1f7b773413a058cfa249..2ce8efb087503dc69081195dea9a5e35081086a9 100644 (file)
@@ -1,11 +1,13 @@
-from collections import defaultdict
+from collections import defaultdict, Set
 from fractions import Fraction
+from functools import wraps
 import sys
 
 from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t
+from libc.stddef cimport wchar_t
 
 from libcpp.pair cimport pair
-from libcpp.set cimport set
+from libcpp.set cimport set as c_set
 from libcpp.string cimport string
 from libcpp.vector cimport vector
 
@@ -17,6 +19,7 @@ from cvc5 cimport DatatypeDecl as c_DatatypeDecl
 from cvc5 cimport DatatypeSelector as c_DatatypeSelector
 from cvc5 cimport Result as c_Result
 from cvc5 cimport RoundingMode as c_RoundingMode
+from cvc5 cimport UnknownExplanation as c_UnknownExplanation
 from cvc5 cimport Op as c_Op
 from cvc5 cimport Solver as c_Solver
 from cvc5 cimport Grammar as c_Grammar
@@ -24,18 +27,30 @@ from cvc5 cimport Sort as c_Sort
 from cvc5 cimport ROUND_NEAREST_TIES_TO_EVEN, ROUND_TOWARD_POSITIVE
 from cvc5 cimport ROUND_TOWARD_NEGATIVE, ROUND_TOWARD_ZERO
 from cvc5 cimport ROUND_NEAREST_TIES_TO_AWAY
+from cvc5 cimport REQUIRES_FULL_CHECK, INCOMPLETE, TIMEOUT
+from cvc5 cimport RESOURCEOUT, MEMOUT, INTERRUPTED
+from cvc5 cimport NO_STATUS, UNSUPPORTED, UNKNOWN_REASON
+from cvc5 cimport OTHER
 from cvc5 cimport Term as c_Term
 from cvc5 cimport hash as c_hash
-
+from cvc5 cimport wstring as c_wstring
+from cvc5 cimport tuple as c_tuple
+from cvc5 cimport get0, get1, get2
 from cvc5kinds cimport Kind as c_Kind
 
+cdef extern from "Python.h":
+    wchar_t* PyUnicode_AsWideCharString(object, Py_ssize_t *)
+    object PyUnicode_FromWideChar(const wchar_t*, Py_ssize_t)
+    void PyMem_Free(void*)
+
 ################################## DECORATORS #################################
 def expand_list_arg(num_req_args=0):
-    '''
+    """
     Creates a decorator that looks at index num_req_args of the args,
     if it's a list, it expands it before calling the function.
-    '''
+    """
     def decorator(func):
+        @wraps(func)
         def wrapper(owner, *args):
             if len(args) == num_req_args + 1 and \
                isinstance(args[num_req_args], list):
@@ -80,14 +95,16 @@ cdef c_hash[c_Term] ctermhash = c_hash[c_Term]()
 
 
 cdef class Datatype:
-    """Wrapper class for :cpp:class:`cvc5::api::Datatype`."""
+    """
+        A cvc5 datatype.
+        Wrapper class for :cpp:class:`cvc5::api::Datatype`.
+    """
     cdef c_Datatype cd
     cdef Solver solver
     def __cinit__(self, Solver solver):
         self.solver = solver
 
     def __getitem__(self, index):
-        """Return a constructor by index or by name."""
         cdef DatatypeConstructor dc = DatatypeConstructor(self.solver)
         if isinstance(index, int) and index >= 0:
             dc.cdc = self.cd[(<int?> index)]
@@ -98,55 +115,76 @@ cdef class Datatype:
         return dc
 
     def getConstructor(self, str name):
-        """Return a constructor by name."""
+        """
+            :param name: the name of the constructor.
+            :return: a constructor by name.
+        """
         cdef DatatypeConstructor dc = DatatypeConstructor(self.solver)
         dc.cdc = self.cd.getConstructor(name.encode())
         return dc
 
     def getConstructorTerm(self, str name):
-        """:return: the term representing the datatype constructor with the given name (see :cpp:func:`Datatype::getConstructorTerm() <cvc5::api::Datatype::getConstructorTerm>`)."""
+        """
+            :param name: the name of the constructor.
+            :return: the term representing the datatype constructor with the given name (see :cpp:func:`Datatype::getConstructorTerm() <cvc5::api::Datatype::getConstructorTerm>`).
+        """
         cdef Term term = Term(self.solver)
         term.cterm = self.cd.getConstructorTerm(name.encode())
         return term
 
     def getSelector(self, str name):
-        """Return a selector by name."""
+        """
+            :param name: the name of the selector..
+            :return: a selector by name.
+        """
         cdef DatatypeSelector ds = DatatypeSelector(self.solver)
         ds.cds = self.cd.getSelector(name.encode())
         return ds
 
+    def getName(self):
+        """
+            :return: the name of the datatype.
+        """
+        return self.cd.getName().decode()
+
     def getNumConstructors(self):
-        """:return: number of constructors."""
+        """
+            :return: number of constructors in this datatype.
+        """
         return self.cd.getNumConstructors()
 
     def isParametric(self):
-        """:return: whether this datatype is parametric."""
+        """:return: True if this datatype is parametric."""
         return self.cd.isParametric()
 
     def isCodatatype(self):
-        """:return: whether this datatype corresponds to a co-datatype."""
+        """:return: True if this datatype corresponds to a co-datatype."""
         return self.cd.isCodatatype()
 
     def isTuple(self):
-        """:return: whether this datatype corresponds to a tuple."""
+        """:return: True if this datatype corresponds to a tuple."""
         return self.cd.isTuple()
 
     def isRecord(self):
-        """:return: whether this datatype corresponds to a record."""
+        """:return: True if this datatype corresponds to a record."""
         return self.cd.isRecord()
 
     def isFinite(self):
-        """:return: whether this datatype is finite."""
+        """:return: True if this datatype is finite."""
         return self.cd.isFinite()
 
     def isWellFounded(self):
-        """:return: whether this datatype is well-founded (see :cpp:func:`Datatype::isWellFounded() <cvc5::api::Datatype::isWellFounded>`)."""
+        """:return: True if this datatype is well-founded (see :cpp:func:`Datatype::isWellFounded() <cvc5::api::Datatype::isWellFounded>`)."""
         return self.cd.isWellFounded()
 
     def hasNestedRecursion(self):
-        """:return: whether this datatype has nested recursion (see :cpp:func:`Datatype::hasNestedRecursion() <cvc5::api::Datatype::hasNestedRecursion>`)."""
+        """:return: True if this datatype has nested recursion (see :cpp:func:`Datatype::hasNestedRecursion() <cvc5::api::Datatype::hasNestedRecursion>`)."""
         return self.cd.hasNestedRecursion()
 
+    def isNull(self):
+        """:return: True if this Datatype is a null object."""
+        return self.cd.isNull()
+
     def __str__(self):
         return self.cd.toString().decode()
 
@@ -161,6 +199,10 @@ cdef class Datatype:
 
 
 cdef class DatatypeConstructor:
+    """
+        A cvc5 datatype constructor.
+        Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructor`.
+    """
     cdef c_DatatypeConstructor cdc
     cdef Solver solver
     def __cinit__(self, Solver solver):
@@ -172,37 +214,72 @@ cdef class DatatypeConstructor:
         if isinstance(index, int) and index >= 0:
             ds.cds = self.cdc[(<int?> index)]
         elif isinstance(index, str):
-            ds.cds = self.cdc[(<const string &> name.encode())]
+            ds.cds = self.cdc[(<const string &> index.encode())]
         else:
             raise ValueError("Expecting a non-negative integer or string")
         return ds
 
     def getName(self):
+        """
+            :return: the name of the constructor.
+        """
         return self.cdc.getName().decode()
 
     def getConstructorTerm(self):
+        """
+            :return: the constructor operator as a term.
+        """
         cdef Term term = Term(self.solver)
         term.cterm = self.cdc.getConstructorTerm()
         return term
 
+    def getSpecializedConstructorTerm(self, Sort retSort):
+        """
+            Specialized method for parametric datatypes (see :cpp:func:`DatatypeConstructor::getSpecializedConstructorTerm() <cvc5::api::DatatypeConstructor::getSpecializedConstructorTerm>`).
+
+            :param retSort: the desired return sort of the constructor
+            :return: the constructor operator as a term.
+        """
+        cdef Term term = Term(self.solver)
+        term.cterm = self.cdc.getSpecializedConstructorTerm(retSort.csort)
+        return term
+
     def getTesterTerm(self):
+        """
+            :return: the tester operator that is related to this constructor, as a term.
+        """
         cdef Term term = Term(self.solver)
         term.cterm = self.cdc.getTesterTerm()
         return term
 
     def getNumSelectors(self):
+        """
+            :return: the number of selecters (so far) of this Datatype constructor.
+        """
         return self.cdc.getNumSelectors()
 
     def getSelector(self, str name):
+        """
+            :param name: the name of the datatype selector.
+            :return: the first datatype selector with the given name
+        """
         cdef DatatypeSelector ds = DatatypeSelector(self.solver)
         ds.cds = self.cdc.getSelector(name.encode())
         return ds
 
     def getSelectorTerm(self, str name):
+        """
+            :param name: the name of the datatype selector.
+            :return: a term representing the firstdatatype selector with the given name.
+        """
         cdef Term term = Term(self.solver)
         term.cterm = self.cdc.getSelectorTerm(name.encode())
         return term
 
+    def isNull(self):
+        """:return: True if this DatatypeConstructor is a null object."""
+        return self.cdc.isNull()
+
     def __str__(self):
         return self.cdc.toString().decode()
 
@@ -217,6 +294,10 @@ cdef class DatatypeConstructor:
 
 
 cdef class DatatypeConstructorDecl:
+    """
+        A cvc5 datatype constructor declaration.
+        Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructorDecl`.
+    """
     cdef c_DatatypeConstructorDecl cddc
     cdef Solver solver
 
@@ -224,11 +305,26 @@ cdef class DatatypeConstructorDecl:
         self.solver = solver
 
     def addSelector(self, str name, Sort sort):
+        """
+            Add datatype selector declaration.
+
+            :param name: the name of the datatype selector declaration to add.
+            :param sort: the range sort of the datatype selector declaration to add.
+        """
         self.cddc.addSelector(name.encode(), sort.csort)
 
     def addSelectorSelf(self, str name):
+        """
+            Add datatype selector declaration whose range sort is the datatype itself.
+
+            :param name: the name of the datatype selector declaration to add.
+        """
         self.cddc.addSelectorSelf(name.encode())
 
+    def isNull(self):
+        """:return: True if this DatatypeConstructorDecl is a null object."""
+        return self.cddc.isNull()
+
     def __str__(self):
         return self.cddc.toString().decode()
 
@@ -237,20 +333,45 @@ cdef class DatatypeConstructorDecl:
 
 
 cdef class DatatypeDecl:
+    """
+        A cvc5 datatype declaration.
+        Wrapper class for :cpp:class:`cvc5::api::DatatypeDecl`.
+    """
     cdef c_DatatypeDecl cdd
     cdef Solver solver
     def __cinit__(self, Solver solver):
         self.solver = solver
 
     def addConstructor(self, DatatypeConstructorDecl ctor):
+        """
+            Add a datatype constructor declaration.
+
+            :param ctor: the datatype constructor declaration to add.
+        """
         self.cdd.addConstructor(ctor.cddc)
 
     def getNumConstructors(self):
+        """
+            :return: number of constructors (so far) for this datatype declaration.
+        """
         return self.cdd.getNumConstructors()
 
     def isParametric(self):
+        """
+            :return: is this datatype declaration parametric?
+        """
         return self.cdd.isParametric()
 
+    def getName(self):
+        """
+            :return: the name of this datatype declaration.
+        """
+        return self.cdd.getName().decode()
+
+    def isNull(self):
+        """:return: True if this DatatypeDecl is a null object."""
+        return self.cdd.isNull()
+
     def __str__(self):
         return self.cdd.toString().decode()
 
@@ -259,6 +380,10 @@ cdef class DatatypeDecl:
 
 
 cdef class DatatypeSelector:
+    """
+        A cvc5 datatype selector.
+        Wrapper class for :cpp:class:`cvc5::api::DatatypeSelector`.
+    """
     cdef c_DatatypeSelector cds
     cdef Solver solver
     def __cinit__(self, Solver solver):
@@ -266,23 +391,39 @@ cdef class DatatypeSelector:
         self.solver = solver
 
     def getName(self):
+        """
+            :return: the name of this datatype selector.
+        """
         return self.cds.getName().decode()
 
     def getSelectorTerm(self):
+        """
+            :return: the selector opeartor of this datatype selector as a term.
+        """
         cdef Term term = Term(self.solver)
         term.cterm = self.cds.getSelectorTerm()
         return term
 
     def getUpdaterTerm(self):
+        """
+            :return: the updater opeartor of this datatype selector as a term.
+        """
         cdef Term term = Term(self.solver)
         term.cterm = self.cds.getUpdaterTerm()
         return term
 
     def getRangeSort(self):
+        """
+            :return: the range sort of this selector.
+        """
         cdef Sort sort = Sort(self.solver)
         sort.csort = self.cds.getRangeSort()
         return sort
 
+    def isNull(self):
+        """:return: True if this DatatypeSelector is a null object."""
+        return self.cds.isNull()
+
     def __str__(self):
         return self.cds.toString().decode()
 
@@ -291,6 +432,13 @@ cdef class DatatypeSelector:
 
 
 cdef class Op:
+    """
+        A cvc5 operator.
+        An operator is a term that represents certain operators,
+        instantiated with its required parameters, e.g.,
+        a term of kind :cpp:enumerator:`BITVECTOR_EXTRACT`.
+        Wrapper class for :cpp:class:`cvc5::api::Op`.
+    """
     cdef c_Op cop
     cdef Solver solver
     def __cinit__(self, Solver solver):
@@ -313,18 +461,36 @@ cdef class Op:
         return cophash(self.cop)
 
     def getKind(self):
+        """
+            :return: the kind of this operator.
+        """
         return kind(<int> self.cop.getKind())
-    
+
     def isIndexed(self):
+        """
+            :return: True iff this operator is indexed.
+        """
         return self.cop.isIndexed()
 
     def isNull(self):
+        """
+            :return: True iff this operator is a null term.
+        """
         return self.cop.isNull()
 
+    def getNumIndices(self):
+        """
+            :return: number of indices of this op.
+        """
+        return self.cop.getNumIndices()
+
     def getIndices(self):
+        """
+            :return: the indices used to create this Op (see :cpp:func:`Op::getIndices() <cvc5::api::Op::getIndices>`).
+        """
         indices = None
         try:
-            indices = self.cop.getIndices[string]()
+            indices = self.cop.getIndices[string]().decode()
         except:
             pass
 
@@ -344,6 +510,10 @@ cdef class Op:
         return indices
 
 cdef class Grammar:
+    """
+        A Sygus Grammar.
+        Wrapper class for :cpp:class:`cvc5::api::Grammar`.
+    """
     cdef c_Grammar  cgrammar
     cdef Solver solver
     def __cinit__(self, Solver solver):
@@ -351,46 +521,100 @@ cdef class Grammar:
         self.cgrammar = c_Grammar()
 
     def addRule(self, Term ntSymbol, Term rule):
+        """
+            Add ``rule`` to the set of rules corresponding to ``ntSymbol``.
+
+            :param ntSymbol: the non-terminal to which the rule is added.
+            :param rule: the rule to add.
+        """
         self.cgrammar.addRule(ntSymbol.cterm, rule.cterm)
 
     def addAnyConstant(self, Term ntSymbol):
+        """
+            Allow ``ntSymbol`` to be an arbitrary constant.
+
+            :param ntSymbol: the non-terminal allowed to be constant.
+        """
         self.cgrammar.addAnyConstant(ntSymbol.cterm)
 
     def addAnyVariable(self, Term ntSymbol):
+        """
+            Allow ``ntSymbol`` to be any input variable to corresponding synth-fun/synth-inv with the same sort as ``ntSymbol``.
+
+            :param ntSymbol: the non-terminal allowed to be any input variable.
+        """
         self.cgrammar.addAnyVariable(ntSymbol.cterm)
 
     def addRules(self, Term ntSymbol, rules):
+        """
+            Add ``ntSymbol`` to the set of rules corresponding to ``ntSymbol``.
+
+            :param ntSymbol: the non-terminal to which the rules are added.
+            :param rules: the rules to add.
+        """
         cdef vector[c_Term] crules
         for r in rules:
             crules.push_back((<Term?> r).cterm)
         self.cgrammar.addRules(ntSymbol.cterm, crules)
 
 cdef class Result:
+    """
+        Encapsulation of a three-valued solver result, with explanations.
+        Wrapper class for :cpp:class:`cvc5::api::Result`.
+    """
     cdef c_Result cr
     def __cinit__(self):
         # gets populated by solver
         self.cr = c_Result()
 
     def isNull(self):
+        """
+            :return: True if Result is empty, i.e., a nullary Result,
+            and not an actual result returned from a :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` (and friends) query.
+        """
         return self.cr.isNull()
 
     def isSat(self):
+        """
+            :return: True if query was a satisfiable :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` or :cpp:func:`Solver::checkSatAssuming() <cvc5::api::Solver::checkSatAssuming>` query.
+        """
         return self.cr.isSat()
 
     def isUnsat(self):
+        """
+            :return: True if query was an usatisfiable :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` or :cpp:func:`Solver::checkSatAssuming() <cvc5::api::Solver::checkSatAssuming>` query.
+        """
         return self.cr.isUnsat()
 
     def isSatUnknown(self):
+        """
+            :return: True if query was a :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` or :cpp:func:`Solver::checkSatAssuming() <cvc5::api::Solver::checkSatAssuming>` query and cvc5 was not able to determine (un)satisfiability.
+        """
         return self.cr.isSatUnknown()
 
     def isEntailed(self):
+        """
+            :return: True if corresponding query was an entailed :cpp:func:`Solver::checkEntailed() <cvc5::api::Solver::checkEntailed>` query.
+        """
         return self.cr.isEntailed()
 
     def isNotEntailed(self):
+        """
+            :return: True if corresponding query was a :cpp:func:`Solver::checkEntailed() <cvc5::api::Solver::checkEntailed>` query that is not entailed.
+        """
         return self.cr.isNotEntailed()
 
     def isEntailmentUnknown(self):
+        """
+            :return: True if query was a :cpp:func:`Solver::checkEntailed() <cvc5::api::Solver::checkEntailed>` query  query and cvc5 was not able to determine if it is entailed.
+        """
         return self.cr.isEntailmentUnknown()
+    
+    def getUnknownExplanation(self):
+        """
+            :return: an explanation for an unknown query result.
+        """
+        return UnknownExplanation(<int> self.cr.getUnknownExplanation())
 
     def __eq__(self, Result other):
         return self.cr == other.cr
@@ -398,9 +622,6 @@ cdef class Result:
     def __ne__(self, Result other):
         return self.cr != other.cr
 
-    def getUnknownExplanation(self):
-        return self.cr.getUnknownExplanation().decode()
-
     def __str__(self):
         return self.cr.toString().decode()
 
@@ -409,6 +630,20 @@ cdef class Result:
 
 
 cdef class RoundingMode:
+    """
+        Rounding modes for floating-point numbers.
+
+        For many floating-point operations, infinitely precise results may not be
+        representable with the number of available bits. Thus, the results are
+        rounded in a certain way to one of the representable floating-point numbers.
+
+        These rounding modes directly follow the SMT-LIB theory for floating-point
+        arithmetic, which in turn is based on IEEE Standard 754 :cite:`IEEE754`.
+        The rounding modes are specified in Sections 4.3.1 and 4.3.2 of the IEEE
+        Standard 754.
+        
+        Wrapper class for :cpp:enum:`cvc5::api::RoundingMode`.
+    """
     cdef c_RoundingMode crm
     cdef str name
     def __cinit__(self, int rm):
@@ -432,81 +667,168 @@ cdef class RoundingMode:
         return self.name
 
 
+cdef class UnknownExplanation:
+    """
+        Wrapper class for :cpp:enum:`cvc5::api::Result::UnknownExplanation`.
+    """
+    cdef c_UnknownExplanation cue
+    cdef str name
+    def __cinit__(self, int ue):
+        # crm always assigned externally
+        self.cue = <c_UnknownExplanation> ue
+        self.name = __unknown_explanations[ue]
+
+    def __eq__(self, UnknownExplanation other):
+        return (<int> self.cue) == (<int> other.cue)
+
+    def __ne__(self, UnknownExplanation other):
+        return not self.__eq__(other)
+
+    def __hash__(self):
+        return hash((<int> self.crm, self.name))
+
+    def __str__(self):
+        return self.name
+
+    def __repr__(self):
+        return self.name
+
+
 cdef class Solver:
+    """Wrapper class for :cpp:class:`cvc5::api::Solver`."""
     cdef c_Solver* csolver
 
     def __cinit__(self):
-        self.csolver = new c_Solver(NULL)
+        self.csolver = new c_Solver()
 
     def __dealloc__(self):
         del self.csolver
 
-    def supportsFloatingPoint(self):
-        return self.csolver.supportsFloatingPoint()
-
     def getBooleanSort(self):
+        """:return: sort Boolean
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.getBooleanSort()
         return sort
 
     def getIntegerSort(self):
+        """:return: sort Integer
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.getIntegerSort()
         return sort
 
+    def getNullSort(self):
+        """:return: sort null
+        """
+        cdef Sort sort = Sort(self)
+        sort.csort = self.csolver.getNullSort()
+        return sort
+
     def getRealSort(self):
+        """:return: sort Real
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.getRealSort()
         return sort
 
     def getRegExpSort(self):
+        """:return: sort of RegExp
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.getRegExpSort()
         return sort
 
     def getRoundingModeSort(self):
+        """:return: sort RoundingMode
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.getRoundingModeSort()
         return sort
 
     def getStringSort(self):
+        """:return: sort String
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.getStringSort()
         return sort
 
     def mkArraySort(self, Sort indexSort, Sort elemSort):
+        """Create an array sort.
+
+        :param indexSort: the array index sort
+        :param elemSort: the array element sort
+        :return: the array sort
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.mkArraySort(indexSort.csort, elemSort.csort)
         return sort
 
     def mkBitVectorSort(self, uint32_t size):
+        """Create a bit-vector sort.
+
+        :param size: the bit-width of the bit-vector sort
+        :return: the bit-vector sort
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.mkBitVectorSort(size)
         return sort
 
     def mkFloatingPointSort(self, uint32_t exp, uint32_t sig):
+        """Create a floating-point sort.
+
+        :param exp: the bit-width of the exponent of the floating-point sort.
+        :param sig: the bit-width of the significand of the floating-point sort.
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.mkFloatingPointSort(exp, sig)
         return sort
 
     def mkDatatypeSort(self, DatatypeDecl dtypedecl):
+        """Create a datatype sort.
+
+        :param dtypedecl: the datatype declaration from which the sort is created
+        :return: the datatype sort
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.mkDatatypeSort(dtypedecl.cdd)
         return sort
 
-    def mkDatatypeSorts(self, list dtypedecls, unresolvedSorts):
-        sorts = []
+    def mkDatatypeSorts(self, list dtypedecls, unresolvedSorts = None):
+        """
+        Create a vector of datatype sorts using unresolved sorts. The names of
+        the datatype declarations in dtypedecls must be distinct.
+
+        This method is called when the DatatypeDecl objects dtypedecls have been
+        built using "unresolved" sorts.
+
+        We associate each sort in unresolvedSorts with exacly one datatype from
+        dtypedecls. In particular, it must have the same name as exactly one
+        datatype declaration in dtypedecls.
+
+        When constructing datatypes, unresolved sorts are replaced by the datatype
+        sort constructed for the datatype declaration it is associated with.
+
+        :param dtypedecls: the datatype declarations from which the sort is created
+        :param unresolvedSorts: the list of unresolved sorts
+        :return: the datatype sorts
+        """
+        if unresolvedSorts == None:
+            unresolvedSorts = set([])
+        else:
+            assert isinstance(unresolvedSorts, Set)
 
+        sorts = []
         cdef vector[c_DatatypeDecl] decls
         for decl in dtypedecls:
             decls.push_back((<DatatypeDecl?> decl).cdd)
 
-        cdef set[c_Sort] usorts
+        cdef c_set[c_Sort] usorts
         for usort in unresolvedSorts:
             usorts.insert((<Sort?> usort).csort)
 
         csorts = self.csolver.mkDatatypeSorts(
-            <const vector[c_DatatypeDecl]&> decls, <const set[c_Sort]&> usorts)
+            <const vector[c_DatatypeDecl]&> decls, <const c_set[c_Sort]&> usorts)
         for csort in csorts:
           sort = Sort(self)
           sort.csort = csort
@@ -515,6 +837,12 @@ cdef class Solver:
         return sorts
 
     def mkFunctionSort(self, sorts, Sort codomain):
+        """ Create function sort.
+
+        :param sorts: the sort of the function arguments
+        :param codomain: the sort of the function return value
+        :return: the function sort
+        """
 
         cdef Sort sort = Sort(self)
         # populate a vector with dereferenced c_Sorts
@@ -532,19 +860,22 @@ cdef class Solver:
         return sort
 
     def mkParamSort(self, symbolname):
+        """ Create a sort parameter.
+
+        :param symbol: the name of the sort
+        :return: the sort parameter
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.mkParamSort(symbolname.encode())
         return sort
 
     @expand_list_arg(num_req_args=0)
     def mkPredicateSort(self, *sorts):
-        '''
-        Supports the following arguments:
-                 Sort mkPredicateSort(List[Sort] sorts)
+        """Create a predicate sort.
 
-                 where sorts can also be comma-separated arguments of
-                  type Sort
-        '''
+        :param sorts: the list of sorts of the predicate, as a list or as distinct arguments.
+        :return: the predicate sort
+        """
         cdef Sort sort = Sort(self)
         cdef vector[c_Sort] v
         for s in sorts:
@@ -554,13 +885,11 @@ cdef class Solver:
 
     @expand_list_arg(num_req_args=0)
     def mkRecordSort(self, *fields):
-        '''
-        Supports the following arguments:
-                Sort mkRecordSort(List[Tuple[str, Sort]] fields)
+        """Create a record sort
 
-                  where fields can also be comma-separated arguments of
-          type Tuple[str, Sort]
-        '''
+        :param fields: the list of fields of the record, as a list or as distinct arguments
+        :return: the record sort
+        """
         cdef Sort sort = Sort(self)
         cdef vector[pair[string, c_Sort]] v
         cdef pair[string, c_Sort] p
@@ -574,39 +903,63 @@ cdef class Solver:
         return sort
 
     def mkSetSort(self, Sort elemSort):
+        """Create a set sort.
+
+        :param elemSort: the sort of the set elements
+        :return: the set sort
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.mkSetSort(elemSort.csort)
         return sort
 
     def mkBagSort(self, Sort elemSort):
+        """Create a bag sort.
+
+        :param elemSort: the sort of the bag elements
+        :return: the bag sort
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.mkBagSort(elemSort.csort)
         return sort
 
     def mkSequenceSort(self, Sort elemSort):
+        """Create a sequence sort.
+
+        :param elemSort: the sort of the sequence elements
+        :return: the sequence sort
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.mkSequenceSort(elemSort.csort)
         return sort
 
     def mkUninterpretedSort(self, str name):
+        """Create an uninterpreted sort.
+
+        :param symbol: the name of the sort
+        :return: the uninterpreted sort
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.mkUninterpretedSort(name.encode())
         return sort
 
     def mkSortConstructorSort(self, str symbol, size_t arity):
+        """Create a sort constructor sort.
+
+        :param symbol: the symbol of the sort
+        :param arity: the arity of the sort
+        :return: the sort constructor sort
+        """
         cdef Sort sort = Sort(self)
         sort.csort =self.csolver.mkSortConstructorSort(symbol.encode(), arity)
         return sort
 
     @expand_list_arg(num_req_args=0)
     def mkTupleSort(self, *sorts):
-        '''
-           Supports the following arguments:
-                Sort mkTupleSort(List[Sort] sorts)
+        """Create a tuple sort.
 
-                 where sorts can also be comma-separated arguments of
-                 type Sort
-        '''
+        :param sorts: of the elements of the tuple, as a list or as distinct arguments
+        :return: the tuple sort
+        """
         cdef Sort sort = Sort(self)
         cdef vector[c_Sort] v
         for s in sorts:
@@ -616,14 +969,15 @@ cdef class Solver:
 
     @expand_list_arg(num_req_args=1)
     def mkTerm(self, kind_or_op, *args):
-        '''
-            Supports the following arguments:
-                    Term mkTerm(Kind kind)
-                    Term mkTerm(Kind kind, Op child1, List[Term] children)
-                    Term mkTerm(Kind kind, List[Term] children)
-
-                where List[Term] can also be comma-separated arguments
-        '''
+        """
+        Supports the following arguments:
+
+        - ``Term mkTerm(Kind kind)``
+        - ``Term mkTerm(Kind kind, Op child1, List[Term] children)``
+        - ``Term mkTerm(Kind kind, List[Term] children)``
+
+        where ``List[Term]`` can also be comma-separated arguments
+        """
         cdef Term term = Term(self)
         cdef vector[c_Term] v
 
@@ -639,61 +993,109 @@ cdef class Solver:
             term.cterm = self.csolver.mkTerm((<Op?> op).cop, v)
         return term
 
-    def mkOp(self, kind k, arg0=None, arg1 = None):
-        '''
+    def mkTuple(self, sorts, terms):
+        """Create a tuple term. Terms are automatically converted if sorts are compatible.
+
+        :param sorts: The sorts of the elements in the tuple
+        :param terms: The elements in the tuple
+        :return: the tuple Term
+        """
+        cdef vector[c_Sort] csorts
+        cdef vector[c_Term] cterms
+
+        for s in sorts:
+            csorts.push_back((<Sort?> s).csort)
+        for s in terms:
+            cterms.push_back((<Term?> s).cterm)
+        cdef Term result = Term(self)
+        result.cterm = self.csolver.mkTuple(csorts, cterms)
+        return result
+
+    @expand_list_arg(num_req_args=0)
+    def mkOp(self, kind k, *args):
+        """
         Supports the following uses:
-                Op mkOp(Kind kind)
-                Op mkOp(Kind kind, Kind k)
-                Op mkOp(Kind kind, const string& arg)
-                Op mkOp(Kind kind, uint32_t arg)
-                Op mkOp(Kind kind, uint32_t arg0, uint32_t arg1)
-        '''
+
+        - ``Op mkOp(Kind kind)``
+        - ``Op mkOp(Kind kind, Kind k)``
+        - ``Op mkOp(Kind kind, const string& arg)``
+        - ``Op mkOp(Kind kind, uint32_t arg)``
+        - ``Op mkOp(Kind kind, uint32_t arg0, uint32_t arg1)``
+        - ``Op mkOp(Kind kind, [uint32_t arg0, ...])`` (used for the TupleProject kind)
+        """
         cdef Op op = Op(self)
+        cdef vector[uint32_t] v
 
-        if arg0 is None:
+        if len(args) == 0:
             op.cop = self.csolver.mkOp(k.k)
-        elif arg1 is None:
-            if isinstance(arg0, kind):
-                op.cop = self.csolver.mkOp(k.k, (<kind?> arg0).k)
-            elif isinstance(arg0, str):
+        elif len(args) == 1:
+            if isinstance(args[0], str):
                 op.cop = self.csolver.mkOp(k.k,
                                            <const string &>
-                                           arg0.encode())
-            elif isinstance(arg0, int):
-                op.cop = self.csolver.mkOp(k.k, <int?> arg0)
+                                           args[0].encode())
+            elif isinstance(args[0], int):
+                op.cop = self.csolver.mkOp(k.k, <int?> args[0])
+            elif isinstance(args[0], list):
+                for a in args[0]:
+                    if a < 0 or a >= 2 ** 31:
+                        raise ValueError("Argument {} must fit in a uint32_t".format(a))
+
+                    v.push_back((<uint32_t?> a))
+                op.cop = self.csolver.mkOp(k.k, <const vector[uint32_t]&> v)   
             else:
                 raise ValueError("Unsupported signature"
-                                 " mkOp: {}".format(" X ".join([str(k), str(arg0)])))
-        else:
-            if isinstance(arg0, int) and isinstance(arg1, int):
-                op.cop = self.csolver.mkOp(k.k, <int> arg0,
-                                                       <int> arg1)
+                                 " mkOp: {}".format(" X ".join([str(k), str(args[0])])))
+        elif len(args) == 2:
+            if isinstance(args[0], int) and isinstance(args[1], int):
+                op.cop = self.csolver.mkOp(k.k, <int> args[0], <int> args[1])
             else:
                 raise ValueError("Unsupported signature"
-                                 " mkOp: {}".format(" X ".join([k, arg0, arg1])))
+                                 " mkOp: {}".format(" X ".join([k, args[0], args[1]])))
         return op
 
     def mkTrue(self):
+        """Create a Boolean true constant.
+
+        :return: the true constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkTrue()
         return term
 
     def mkFalse(self):
+        """Create a Boolean false constant.
+
+        :return: the false constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkFalse()
         return term
 
     def mkBoolean(self, bint val):
+        """Create a Boolean constant.
+
+        :return: the Boolean constant
+        :param val: the value of the constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkBoolean(val)
         return term
 
     def mkPi(self):
+        """Create a constant representing the number Pi.
+
+        :return: a constant representing Pi
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkPi()
         return term
 
     def mkInteger(self, val):
+        """Create an integer constant.
+
+        :param val: representation of the constant: either a string or integer
+        :return: a constant of sort Integer
+        """
         cdef Term term = Term(self)
         if isinstance(val, str):
             term.cterm = self.csolver.mkInteger(<const string &> str(val).encode())
@@ -703,6 +1105,20 @@ cdef class Solver:
         return term
 
     def mkReal(self, val, den=None):
+        """Create a real constant.
+
+        :param: `val` the value of the term. Can be an integer, float, or string. It will be formatted as a string before the term is built.
+        :param: `den` if not None, the value is `val`/`den`
+        :return: a real term with literal value
+
+        Can be used in various forms:
+
+        - Given a string ``"N/D"`` constructs the corresponding rational.
+        - Given a string ``"W.D"`` constructs the reduction of ``(W * P + D)/P``, where ``P`` is the appropriate power of 10.
+        - Given a float ``f``, constructs the rational matching ``f``'s string representation. This means that ``mkReal(0.3)`` gives ``3/10`` and not the IEEE-754 approximation of ``3/10``.
+        - Given a string ``"W"`` or an integer, constructs that integer.
+        - Given two strings and/or integers ``N`` and ``D``, constructs ``N/D``.
+        """
         cdef Term term = Term(self)
         if den is None:
             term.cterm = self.csolver.mkReal(str(val).encode())
@@ -715,132 +1131,272 @@ cdef class Solver:
         return term
 
     def mkRegexpEmpty(self):
+        """Create a regular expression empty term.
+
+        :return: the empty term
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkRegexpEmpty()
         return term
 
     def mkRegexpSigma(self):
+        """Create a regular expression sigma term.
+
+        :return: the sigma term
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkRegexpSigma()
         return term
 
     def mkEmptySet(self, Sort s):
+        """Create a constant representing an empty set of the given sort.
+
+        :param sort: the sort of the set elements.
+        :return: the empty set constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkEmptySet(s.csort)
         return term
 
+    def mkEmptyBag(self, Sort s):
+        """Create a constant representing an empty bag of the given sort.
+
+        :param sort: the sort of the bag elements.
+        :return: the empty bag constant
+        """
+        cdef Term term = Term(self)
+        term.cterm = self.csolver.mkEmptyBag(s.csort)
+        return term
 
     def mkSepNil(self, Sort sort):
+        """Create a separation logic nil term.
+
+        :param sort: the sort of the nil term
+        :return: the separation logic nil term
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkSepNil(sort.csort)
         return term
 
-    def mkString(self, str_or_vec):
+    def mkString(self, str s, useEscSequences = None):
+        """
+        Create a String constant from a `str` which may contain SMT-LIB
+        compatible escape sequences like ``\\u1234`` to encode unicode characters.
+
+        :param s: the string this constant represents
+        :param useEscSequences: determines whether escape sequences in `s` should be converted to the corresponding unicode character
+        :return: the String constant
+        """
         cdef Term term = Term(self)
-        cdef vector[unsigned] v
-        if isinstance(str_or_vec, str):
-            for u in str_or_vec:
-                v.push_back(<unsigned> ord(u))
-            term.cterm = self.csolver.mkString(<const vector[unsigned]&> v)
-        elif isinstance(str_or_vec, list):
-            for u in str_or_vec:
-                if not isinstance(u, int):
-                    raise ValueError("List should contain ints but got: {}"
-                                     .format(str_or_vec))
-                v.push_back(<unsigned> u)
-            term.cterm = self.csolver.mkString(<const vector[unsigned]&> v)
+        cdef Py_ssize_t size
+        cdef wchar_t* tmp = PyUnicode_AsWideCharString(s, &size)
+        if isinstance(useEscSequences, bool):
+            term.cterm = self.csolver.mkString(
+                s.encode(), <bint> useEscSequences)
         else:
-            raise ValueError("Expected string or vector of ASCII codes"
-                             " but got: {}".format(str_or_vec))
+            term.cterm = self.csolver.mkString(c_wstring(tmp, size))
+        PyMem_Free(tmp)
         return term
 
     def mkEmptySequence(self, Sort sort):
+        """Create an empty sequence of the given element sort.
+
+        :param sort: The element sort of the sequence.
+        :return: the empty sequence with given element sort.
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkEmptySequence(sort.csort)
         return term
 
     def mkUniverseSet(self, Sort sort):
+        """Create a universe set of the given sort.
+
+        :param sort: the sort of the set elements
+        :return: the universe set constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkUniverseSet(sort.csort)
         return term
 
-    def mkBitVector(self, size_or_str, val = None):
+    @expand_list_arg(num_req_args=0)
+    def mkBitVector(self, *args):
+        """
+        Supports the following arguments:
+
+        - ``Term mkBitVector(int size, int val=0)``
+        - ``Term mkBitVector(int size, string val, int base)``
+
+        :return: a bit-vector literal term
+        :param: `size` an integer size.
+        :param: `val` an integer representating the value, in the first form. In the second form, a string representing the value.
+        :param: `base` the base of the string representation (second form only)
+        """
         cdef Term term = Term(self)
-        if isinstance(size_or_str, int):
-            if val is None:
-                term.cterm = self.csolver.mkBitVector(<uint32_t> size_or_str)
-            else:
-                term.cterm = self.csolver.mkBitVector(<uint32_t> size_or_str,
-                                                      <const string &> str(val).encode(),
-                                                      10)
-        elif isinstance(size_or_str, str):
-            # handle default value
-            if val is None:
-                term.cterm = self.csolver.mkBitVector(
-                    <const string &> size_or_str.encode())
-            else:
-                term.cterm = self.csolver.mkBitVector(
-                    <const string &> size_or_str.encode(), <uint32_t> val)
+        if len(args) == 0:
+            raise ValueError("Missing arguments to mkBitVector")
+        size = args[0]
+        if not isinstance(size, int):
+            raise ValueError(
+                "Invalid first argument to mkBitVector '{}', "
+                "expected bit-vector size".format(size))
+        if len(args) == 1:
+            term.cterm = self.csolver.mkBitVector(<uint32_t> size)
+        elif len(args) == 2:
+            val = args[1]
+            if not isinstance(val, int):
+                raise ValueError(
+                    "Invalid second argument to mkBitVector '{}', "
+                    "expected integer value".format(size))
+            term.cterm = self.csolver.mkBitVector(
+                <uint32_t> size, <uint32_t> val)
+        elif len(args) == 3:
+            val = args[1]
+            base = args[2]
+            if not isinstance(val, str):
+                raise ValueError(
+                    "Invalid second argument to mkBitVector '{}', "
+                    "expected value string".format(size))
+            if not isinstance(base, int):
+                raise ValueError(
+                    "Invalid third argument to mkBitVector '{}', "
+                    "expected base given as integer".format(size))
+            term.cterm = self.csolver.mkBitVector(
+                <uint32_t> size,
+                <const string&> str(val).encode(),
+                <uint32_t> base)
         else:
-            raise ValueError("Unexpected inputs {} to"
-                             " mkBitVector".format((size_or_str, val)))
+            raise ValueError("Unexpected inputs to mkBitVector")
         return term
 
     def mkConstArray(self, Sort sort, Term val):
+        """
+        Create a constant array with the provided constant value stored at every
+        index
+
+        :param sort: the sort of the constant array (must be an array sort)
+        :param val: the constant value to store (must match the sort's element sort)
+        :return: the constant array term
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkConstArray(sort.csort, val.cterm)
         return term
 
     def mkPosInf(self, int exp, int sig):
+        """Create a positive infinity floating-point constant.
+
+        :param exp: Number of bits in the exponent
+        :param sig: Number of bits in the significand
+        :return: the floating-point constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkPosInf(exp, sig)
         return term
 
     def mkNegInf(self, int exp, int sig):
+        """Create a negative infinity floating-point constant.
+
+        :param exp: Number of bits in the exponent
+        :param sig: Number of bits in the significand
+        :return: the floating-point constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkNegInf(exp, sig)
         return term
 
     def mkNaN(self, int exp, int sig):
+        """Create a not-a-number (NaN) floating-point constant.
+
+        :param exp: Number of bits in the exponent
+        :param sig: Number of bits in the significand
+        :return: the floating-point constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkNaN(exp, sig)
         return term
 
     def mkPosZero(self, int exp, int sig):
+        """Create a positive zero (+0.0) floating-point constant.
+
+        :param exp: Number of bits in the exponent
+        :param sig: Number of bits in the significand
+        :return: the floating-point constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkPosZero(exp, sig)
         return term
 
     def mkNegZero(self, int exp, int sig):
+        """Create a negative zero (+0.0) floating-point constant.
+
+        :param exp: Number of bits in the exponent
+        :param sig: Number of bits in the significand
+        :return: the floating-point constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkNegZero(exp, sig)
         return term
 
     def mkRoundingMode(self, RoundingMode rm):
+        """Create a roundingmode constant.
+        
+        :param rm: the floating point rounding mode this constant represents
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkRoundingMode(<c_RoundingMode> rm.crm)
         return term
 
     def mkUninterpretedConst(self, Sort sort, int index):
+        """Create uninterpreted constant.
+
+        :param sort: Sort of the constant
+        :param index: Index of the constant
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkUninterpretedConst(sort.csort, index)
         return term
 
     def mkAbstractValue(self, index):
+        """
+        Create an abstract value constant.
+        The given index needs to be positive.
+
+        :param index: Index of the abstract value
+        """
         cdef Term term = Term(self)
         try:
             term.cterm = self.csolver.mkAbstractValue(str(index).encode())
         except:
-            raise ValueError("mkAbstractValue expects a str representing a number"
-                             " or an int, but got{}".format(index))
+            raise ValueError(
+                "mkAbstractValue expects a str representing a number"
+                " or an int, but got{}".format(index))
         return term
 
     def mkFloatingPoint(self, int exp, int sig, Term val):
+        """Create a floating-point constant.
+
+        :param exp: Size of the exponent
+        :param sig: Size of the significand
+        :param val: Value of the floating-point constant as a bit-vector term
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkFloatingPoint(exp, sig, val.cterm)
         return term
 
     def mkConst(self, Sort sort, symbol=None):
+        """
+        Create (first-order) constant (0-arity function symbol).
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( declare-const <symbol> <sort> )
+            ( declare-fun <symbol> ( ) <sort> )
+
+        :param sort: the sort of the constant
+        :param symbol: the name of the constant. If None, a default symbol is used.
+        :return: the first-order constant
+        """
         cdef Term term = Term(self)
         if symbol is None:
             term.cterm = self.csolver.mkConst(sort.csort)
@@ -850,6 +1406,14 @@ cdef class Solver:
         return term
 
     def mkVar(self, Sort sort, symbol=None):
+        """
+        Create a bound variable to be used in a binder (i.e. a quantifier, a
+        lambda, or a witness binder).
+
+        :param sort: the sort of the variable
+        :param symbol: the name of the variable
+        :return: the variable
+        """
         cdef Term term = Term(self)
         if symbol is None:
             term.cterm = self.csolver.mkVar(sort.csort)
@@ -859,11 +1423,21 @@ cdef class Solver:
         return term
 
     def mkDatatypeConstructorDecl(self, str name):
+        """
+        :return: a datatype constructor declaration
+        :param: `name` the constructor's name
+        """
         cdef DatatypeConstructorDecl ddc = DatatypeConstructorDecl(self)
         ddc.cddc = self.csolver.mkDatatypeConstructorDecl(name.encode())
         return ddc
 
     def mkDatatypeDecl(self, str name, sorts_or_bool=None, isCoDatatype=None):
+        """Create a datatype declaration.
+
+        :param name: the name of the datatype
+        :param isCoDatatype: true if a codatatype is to be constructed
+        :return: the DatatypeDecl
+        """
         cdef DatatypeDecl dd = DatatypeDecl(self)
         cdef vector[c_Sort] v
 
@@ -908,19 +1482,57 @@ cdef class Solver:
         return dd
 
     def simplify(self, Term t):
+        """
+        Simplify a formula without doing "much" work.  Does not involve the
+        SAT Engine in the simplification, but uses the current definitions,
+        assertions, and the current partial model, if one has been constructed.
+        It also involves theory normalization.
+
+        :param t: the formula to simplify
+        :return: the simplified formula
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.simplify(t.cterm)
         return term
 
     def assertFormula(self, Term term):
+        """ Assert a formula
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( assert <term> )
+
+        :param term: the formula to assert
+        """
         self.csolver.assertFormula(term.cterm)
 
     def checkSat(self):
+        """
+        Check satisfiability.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( check-sat )
+
+        :return: the result of the satisfiability check.
+        """
         cdef Result r = Result()
         r.cr = self.csolver.checkSat()
         return r
 
     def mkSygusGrammar(self, boundVars, ntSymbols):
+        """
+        Create a SyGuS grammar. The first non-terminal is treated as the
+        starting non-terminal, so the order of non-terminals matters.
+
+        :param boundVars: the parameters to corresponding synth-fun/synth-inv
+        :param ntSymbols: the pre-declaration of the non-terminal symbols
+        :return: the grammar
+        """
         cdef Grammar grammar = Grammar(self)
         cdef vector[c_Term] bvc
         cdef vector[c_Term] ntc
@@ -932,17 +1544,70 @@ cdef class Solver:
         return grammar
 
     def mkSygusVar(self, Sort sort, str symbol=""):
+        """Append symbol to the current list of universal variables.
+
+        SyGuS v2:
+
+        .. code-block:: smtlib
+
+            ( declare-var <symbol> <sort> )
+
+        :param sort: the sort of the universal variable
+        :param symbol: the name of the universal variable
+        :return: the universal variable
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.mkSygusVar(sort.csort, symbol.encode())
         return term
 
     def addSygusConstraint(self, Term t):
+        """
+        Add a formula to the set of SyGuS constraints.
+
+        SyGuS v2:
+
+        .. code-block:: smtlib
+
+            ( constraint <term> )
+
+        :param term: the formula to add as a constraint
+        """
         self.csolver.addSygusConstraint(t.cterm)
 
     def addSygusInvConstraint(self, Term inv_f, Term pre_f, Term trans_f, Term post_f):
+        """
+        Add a set of SyGuS constraints to the current state that correspond to an
+        invariant synthesis problem.
+
+        SyGuS v2:
+
+        .. code-block:: smtlib
+
+            ( inv-constraint <inv> <pre> <trans> <post> )
+
+        :param inv: the function-to-synthesize
+        :param pre: the pre-condition
+        :param trans: the transition relation
+        :param post: the post-condition
+        """
         self.csolver.addSygusInvConstraint(inv_f.cterm, pre_f.cterm, trans_f.cterm, post_f.cterm)
 
     def synthFun(self, str symbol, bound_vars, Sort sort, Grammar grammar=None):
+        """
+        Synthesize n-ary function following specified syntactic constraints.
+
+        SyGuS v2:
+
+        .. code-block:: smtlib
+
+            ( synth-fun <symbol> ( <boundVars>* ) <sort> <g> )
+
+        :param symbol: the name of the function
+        :param boundVars: the parameters to this function
+        :param sort: the sort of the return value of this function
+        :param grammar: the syntactic constraints
+        :return: the function
+        """
         cdef Term term = Term(self)
         cdef vector[c_Term] v
         for bv in bound_vars:
@@ -954,16 +1619,70 @@ cdef class Solver:
         return term
 
     def checkSynth(self):
+        """
+        Try to find a solution for the synthesis conjecture corresponding to the
+        current list of functions-to-synthesize, universal variables and
+        constraints.
+
+        SyGuS v2:
+
+        .. code-block:: smtlib
+
+            ( check-synth )
+
+        :return: the result of the synthesis conjecture.
+        """
         cdef Result r = Result()
         r.cr = self.csolver.checkSynth()
         return r
 
     def getSynthSolution(self, Term term):
+        """
+        Get the synthesis solution of the given term. This method should be
+        called immediately after the solver answers unsat for sygus input.
+
+        :param term: the term for which the synthesis solution is queried
+        :return: the synthesis solution of the given term
+        """
         cdef Term t = Term(self)
         t.cterm = self.csolver.getSynthSolution(term.cterm)
         return t
 
+    def getSynthSolutions(self, list terms):
+        """
+        Get the synthesis solutions of the given terms. This method should be
+        called immediately after the solver answers unsat for sygus input.
+
+        :param terms: the terms for which the synthesis solutions is queried
+        :return: the synthesis solutions of the given terms
+        """
+        result = []
+        cdef vector[c_Term] vec
+        for t in terms:
+            vec.push_back((<Term?> t).cterm)
+        cresult = self.csolver.getSynthSolutions(vec)
+        for s in cresult:
+            term = Term(self)
+            term.cterm = s
+            result.append(term)
+        return result
+
+
     def synthInv(self, symbol, bound_vars, Grammar grammar=None):
+        """
+        Synthesize invariant.
+
+        SyGuS v2:
+
+        .. code-block:: smtlib
+
+            ( synth-inv <symbol> ( <boundVars>* ) <grammar> )
+
+        :param symbol: the name of the invariant
+        :param boundVars: the parameters to this invariant
+        :param grammar: the syntactic constraints
+        :return: the invariant
+        """
         cdef Term term = Term(self)
         cdef vector[c_Term] v
         for bv in bound_vars:
@@ -974,18 +1693,19 @@ cdef class Solver:
             term.cterm = self.csolver.synthInv(symbol.encode(), <const vector[c_Term]&> v, grammar.cgrammar)
         return term
 
-    def printSynthSolution(self):
-        self.csolver.printSynthSolution(cout)
-
     @expand_list_arg(num_req_args=0)
     def checkSatAssuming(self, *assumptions):
-        '''
-            Supports the following arguments:
-                 Result checkSatAssuming(List[Term] assumptions)
+        """ Check satisfiability assuming the given formula.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
 
-                 where assumptions can also be comma-separated arguments of
-                 type (boolean) Term
-        '''
+            ( check-sat-assuming ( <prop_literal> ) )
+
+        :param assumptions: the formulas to assume, as a list or as distinct arguments
+        :return: the result of the satisfiability check.
+        """
         cdef Result r = Result()
         # used if assumptions is a list of terms
         cdef vector[c_Term] v
@@ -996,13 +1716,11 @@ cdef class Solver:
 
     @expand_list_arg(num_req_args=0)
     def checkEntailed(self, *assumptions):
-        '''
-            Supports the following arguments:
-                 Result checkEntailed(List[Term] assumptions)
+        """Check entailment of the given formula w.r.t. the current set of assertions.
 
-                 where assumptions can also be comma-separated arguments of
-                 type (boolean) Term
-        '''
+        :param terms: the formulas to check entailment for, as a list or as distinct arguments
+        :return: the result of the entailment check.
+        """
         cdef Result r = Result()
         # used if assumptions is a list of terms
         cdef vector[c_Term] v
@@ -1013,13 +1731,19 @@ cdef class Solver:
 
     @expand_list_arg(num_req_args=1)
     def declareDatatype(self, str symbol, *ctors):
-        '''
-            Supports the following arguments:
-                 Sort declareDatatype(str symbol, List[Term] ctors)
+        """
+        Create datatype sort.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
 
-                 where ctors can also be comma-separated arguments of
-                  type DatatypeConstructorDecl
-        '''
+            ( declare-datatype <symbol> <datatype_decl> )
+
+        :param symbol: the name of the datatype sort
+        :param ctors: the constructor declarations of the datatype sort, as a list or as distinct arguments
+        :return: the datatype sort
+        """
         cdef Sort sort = Sort(self)
         cdef vector[c_DatatypeConstructorDecl] v
 
@@ -1029,6 +1753,19 @@ cdef class Solver:
         return sort
 
     def declareFun(self, str symbol, list sorts, Sort sort):
+        """Declare n-ary function symbol.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( declare-fun <symbol> ( <sort>* ) <sort> )
+
+        :param symbol: the name of the function
+        :param sorts: the sorts of the parameters to this function
+        :param sort: the sort of the return value of this function
+        :return: the function
+        """
         cdef Term term = Term(self)
         cdef vector[c_Sort] v
         for s in sorts:
@@ -1039,18 +1776,44 @@ cdef class Solver:
         return term
 
     def declareSort(self, str symbol, int arity):
+        """Declare uninterpreted sort.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( declare-sort <symbol> <numeral> )
+
+        :param symbol: the name of the sort
+        :param arity: the arity of the sort
+        :return: the sort
+        """
         cdef Sort sort = Sort(self)
         sort.csort = self.csolver.declareSort(symbol.encode(), arity)
         return sort
 
     def defineFun(self, sym_or_fun, bound_vars, sort_or_term, t=None, glbl=False):
-        '''
+        """
+        Define n-ary function.
         Supports two uses:
-                Term defineFun(str symbol, List[Term] bound_vars,
-                               Sort sort, Term term, bool glbl)
-                Term defineFun(Term fun, List[Term] bound_vars,
-                               Term term, bool glbl)
-        '''
+
+        - ``Term defineFun(str symbol, List[Term] bound_vars, Sort sort, Term term, bool glbl)``
+        - ``Term defineFun(Term fun, List[Term] bound_vars, Term term, bool glbl)``
+
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( define-fun <function_def> )
+
+        :param symbol: the name of the function
+        :param bound_vars: the parameters to this function
+        :param sort: the sort of the return value of this function
+        :param term: the function body
+        :param global: determines whether this definition is global (i.e. persists when popping the context)
+        :return: the function
+        """
         cdef Term term = Term(self)
         cdef vector[c_Term] v
         for bv in bound_vars:
@@ -1071,13 +1834,28 @@ cdef class Solver:
         return term
 
     def defineFunRec(self, sym_or_fun, bound_vars, sort_or_term, t=None, glbl=False):
-        '''
+        """Define recursive functions.
+
         Supports two uses:
-                Term defineFunRec(str symbol, List[Term] bound_vars,
-                               Sort sort, Term term, bool glbl)
-                Term defineFunRec(Term fun, List[Term] bound_vars,
-                               Term term, bool glbl)
-        '''
+
+        - ``Term defineFunRec(str symbol, List[Term] bound_vars, Sort sort, Term term, bool glbl)``
+        - ``Term defineFunRec(Term fun, List[Term] bound_vars, Term term, bool glbl)``
+
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( define-funs-rec ( <function_decl>^n ) ( <term>^n ) )
+
+        Create elements of parameter ``funs`` with mkConst().
+
+        :param funs: the sorted functions
+        :param bound_vars: the list of parameters to the functions
+        :param terms: the list of function bodies of the functions
+        :param global: determines whether this definition is global (i.e. persists when popping the context)
+        :return: the function
+        """
         cdef Term term = Term(self)
         cdef vector[c_Term] v
         for bv in bound_vars:
@@ -1098,6 +1876,22 @@ cdef class Solver:
         return term
 
     def defineFunsRec(self, funs, bound_vars, terms):
+        """Define recursive functions.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( define-funs-rec ( <function_decl>^n ) ( <term>^n ) )
+
+        Create elements of parameter ``funs`` with mkConst().
+
+        :param funs: the sorted functions
+        :param bound_vars: the list of parameters to the functions
+        :param terms: the list of function bodies of the functions
+        :param global: determines whether this definition is global (i.e. persists when popping the context)
+        :return: the function
+        """
         cdef vector[c_Term] vf
         cdef vector[vector[c_Term]] vbv
         cdef vector[c_Term] vt
@@ -1116,6 +1910,16 @@ cdef class Solver:
             vf.push_back((<Term?> t).cterm)
 
     def getAssertions(self):
+        """Get the list of asserted formulas.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( get-assertions )
+
+        :return: the list of asserted formulas
+        """
         assertions = []
         for a in self.csolver.getAssertions():
             term = Term(self)
@@ -1124,12 +1928,47 @@ cdef class Solver:
         return assertions
 
     def getInfo(self, str flag):
+        """Get info from the solver.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( get-info <info_flag> )
+
+        :param flag: the info flag
+        :return: the info
+        """
         return self.csolver.getInfo(flag.encode())
 
     def getOption(self, str option):
+        """Get the value of a given option.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( get-option <keyword> )
+
+        :param option: the option for which the value is queried
+        :return: a string representation of the option value
+        """
         return self.csolver.getOption(option.encode())
 
     def getUnsatAssumptions(self):
+        """
+        Get the set of unsat ("failed") assumptions.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( get-unsat-assumptions )
+
+        Requires to enable option :ref:`produce-unsat-assumptions <lbl-option-produce-unsat-assumptions>`.
+
+        :return: the set of unsat assumptions.
+        """
         assumptions = []
         for a in self.csolver.getUnsatAssumptions():
             term = Term(self)
@@ -1138,6 +1977,18 @@ cdef class Solver:
         return assumptions
 
     def getUnsatCore(self):
+        """Get the unsatisfiable core.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( get-unsat-core )
+
+        Requires to enable option :ref:`produce-unsat-cores <lbl-option-produce-unsat-cores>`.
+
+        :return: a set of terms representing the unsatisfiable core
+        """
         core = []
         for a in self.csolver.getUnsatCore():
             term = Term(self)
@@ -1146,24 +1997,92 @@ cdef class Solver:
         return core
 
     def getValue(self, Term t):
+        """Get the value of the given term in the current model.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( get-value ( <term> ) )
+
+        :param term: the term for which the value is queried
+        :return: the value of the given term
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.getValue(t.cterm)
         return term
 
+    def getModelDomainElements(self, Sort s):
+        """
+        Get the domain elements of uninterpreted sort s in the current model. The
+        current model interprets s as the finite sort whose domain elements are
+        given in the return value of this method.
+
+        :param s: The uninterpreted sort in question
+        :return: the domain elements of s in the current model
+        """
+        result = []
+        cresult = self.csolver.getModelDomainElements(s.csort)
+        for e in cresult:
+            term = Term(self)
+            term.cterm = e
+            result.append(term)
+        return result
+
+    def isModelCoreSymbol(self, Term v):
+        """
+        This returns false if the model value of free constant v was not
+        essential for showing the satisfiability of the last call to checkSat
+        using the current model. This method will only return false (for any v)
+        if the model-cores option has been set.
+
+        :param v: The term in question
+        :return: true if v is a model core symbol
+        """
+        return self.csolver.isModelCoreSymbol(v.cterm)
+
     def getSeparationHeap(self):
+        """When using separation logic, obtain the term for the heap.
+
+        :return: The term for the heap
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.getSeparationHeap()
         return term
 
-    def declareSeparationHeap(self, Sort locType, Sort dataType):
-        self.csolver.declareSeparationHeap(locType.csort, dataType.csort)
-
     def getSeparationNilTerm(self):
+        """When using separation logic, obtain the term for nil.
+
+        :return: The term for nil
+        """
         cdef Term term = Term(self)
         term.cterm = self.csolver.getSeparationNilTerm()
         return term
 
+    def declareSeparationHeap(self, Sort locType, Sort dataType):
+        """
+        When using separation logic, this sets the location sort and the
+        datatype sort to the given ones. This method should be invoked exactly
+        once, before any separation logic constraints are provided.
+
+        :param locSort: The location sort of the heap
+        :param dataSort: The data sort of the heap
+        """
+        self.csolver.declareSeparationHeap(locType.csort, dataType.csort)
+
     def declarePool(self, str symbol, Sort sort, initValue):
+        """Declare a symbolic pool of terms with the given initial value.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( declare-pool <symbol> <sort> ( <term>* ) )
+
+        :param symbol: The name of the pool
+        :param sort: The sort of the elements of the pool.
+        :param initValue: The initial value of the pool
+        """
         cdef Term term = Term(self)
         cdef vector[c_Term] niv
         for v in initValue:
@@ -1172,21 +2091,83 @@ cdef class Solver:
         return term
 
     def pop(self, nscopes=1):
+        """Pop ``nscopes`` level(s) from the assertion stack.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( pop <numeral> )
+
+        :param nscopes: the number of levels to pop
+        """
         self.csolver.pop(nscopes)
 
     def push(self, nscopes=1):
+        """ Push ``nscopes`` level(s) to the assertion stack.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( push <numeral> )
+
+        :param nscopes: the number of levels to push
+        """
         self.csolver.push(nscopes)
 
     def resetAssertions(self):
+        """
+        Remove all assertions.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( reset-assertions )
+
+        """
         self.csolver.resetAssertions()
 
     def setInfo(self, str keyword, str value):
+        """Set info.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( set-info <attribute> )
+
+        :param keyword: the info flag
+        :param value: the value of the info flag
+        """
         self.csolver.setInfo(keyword.encode(), value.encode())
 
     def setLogic(self, str logic):
+        """Set logic.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( set-logic <symbol> )
+
+        :param logic: the logic to set
+        """
         self.csolver.setLogic(logic.encode())
 
     def setOption(self, str option, str value):
+        """Set option.
+
+        SMT-LIB:
+
+        .. code-block:: smtlib
+
+            ( set-option <option> )
+
+        :param option: the option name
+        :param value: the option value
+        """
         self.csolver.setOption(option.encode(), value.encode())
 
 
@@ -1224,6 +2205,9 @@ cdef class Sort:
     def __hash__(self):
         return csorthash(self.csort)
 
+    def isNull(self):
+        return self.csort.isNull()
+
     def isBoolean(self):
         return self.csort.isBoolean()
 
@@ -1263,6 +2247,9 @@ cdef class Sort:
     def isTester(self):
         return self.csort.isTester()
 
+    def isUpdater(self):
+        return self.csort.isUpdater()
+
     def isFunction(self):
         return self.csort.isFunction()
 
@@ -1283,7 +2270,7 @@ cdef class Sort:
 
     def isBag(self):
         return self.csort.isBag()
-    
+
     def isSequence(self):
         return self.csort.isSequence()
 
@@ -1353,7 +2340,7 @@ cdef class Sort:
         cdef Sort sort = Sort(self.solver)
         sort.csort = self.csort.getTesterCodomainSort()
         return sort
-    
+
     def getFunctionArity(self):
         return self.csort.getFunctionArity()
 
@@ -1415,14 +2402,14 @@ cdef class Sort:
     def getSortConstructorArity(self):
         return self.csort.getSortConstructorArity()
 
-    def getBVSize(self):
-        return self.csort.getBVSize()
+    def getBitVectorSize(self):
+        return self.csort.getBitVectorSize()
 
-    def getFPExponentSize(self):
-        return self.csort.getFPExponentSize()
+    def getFloatingPointExponentSize(self):
+        return self.csort.getFloatingPointExponentSize()
 
-    def getFPSignificandSize(self):
-        return self.csort.getFPSignificandSize()
+    def getFloatingPointSignificandSize(self):
+        return self.csort.getFloatingPointSignificandSize()
 
     def getDatatypeParamSorts(self):
         param_sorts = []
@@ -1515,7 +2502,7 @@ cdef class Term:
         # lists for substitutions
         cdef vector[c_Term] ces
         cdef vector[c_Term] creplacements
-        
+
         # normalize the input parameters to be lists
         if isinstance(term_or_list_1, list):
             assert isinstance(term_or_list_2, list)
@@ -1534,7 +2521,7 @@ cdef class Term:
             # add the single elements to the vectors
             ces.push_back((<Term?> term_or_list_1).cterm)
             creplacements.push_back((<Term?> term_or_list_2).cterm)
-        
+
         # call the API substitute method with lists
         term.cterm = self.cterm.substitute(ces, creplacements)
         return term
@@ -1550,19 +2537,6 @@ cdef class Term:
     def isNull(self):
         return self.cterm.isNull()
 
-    def getConstArrayBase(self):
-        cdef Term term = Term(self.solver)
-        term.cterm = self.cterm.getConstArrayBase()
-        return term
-
-    def getConstSequenceElements(self):
-        elems = []
-        for e in self.cterm.getConstSequenceElements():
-            term = Term(self.solver)
-            term.cterm = e
-            elems.append(term)
-        return elems
-
     def notTerm(self):
         cdef Term term = Term(self.solver)
         term.cterm = self.cterm.notTerm()
@@ -1598,11 +2572,122 @@ cdef class Term:
         term.cterm = self.cterm.iteTerm(then_t.cterm, else_t.cterm)
         return term
 
-    def isInteger(self):
-        return self.cterm.isInteger()
-    
+    def isConstArray(self):
+        return self.cterm.isConstArray()
+
+    def getConstArrayBase(self):
+        cdef Term term = Term(self.solver)
+        term.cterm = self.cterm.getConstArrayBase()
+        return term
+
+    def isBooleanValue(self):
+        return self.cterm.isBooleanValue()
+
+    def getBooleanValue(self):
+        return self.cterm.getBooleanValue()
+
+    def isStringValue(self):
+        return self.cterm.isStringValue()
+
+    def getStringValue(self):
+        cdef Py_ssize_t size
+        cdef c_wstring s = self.cterm.getStringValue()
+        return PyUnicode_FromWideChar(s.data(), s.size())
+
+    def isIntegerValue(self):
+        return self.cterm.isIntegerValue()
+    def isAbstractValue(self):
+        return self.cterm.isAbstractValue()
+
+    def getAbstractValue(self):
+        return self.cterm.getAbstractValue().decode()
+
+    def isFloatingPointPosZero(self):
+        return self.cterm.isFloatingPointPosZero()
+
+    def isFloatingPointNegZero(self):
+        return self.cterm.isFloatingPointNegZero()
+
+    def isFloatingPointPosInf(self):
+        return self.cterm.isFloatingPointPosInf()
+
+    def isFloatingPointNegInf(self):
+        return self.cterm.isFloatingPointNegInf()
+
+    def isFloatingPointNaN(self):
+        return self.cterm.isFloatingPointNaN()
+
+    def isFloatingPointValue(self):
+        return self.cterm.isFloatingPointValue()
+
+    def getFloatingPointValue(self):
+        cdef c_tuple[uint32_t, uint32_t, c_Term] t = self.cterm.getFloatingPointValue()
+        cdef Term term = Term(self.solver)
+        term.cterm = get2(t)
+        return (get0(t), get1(t), term)
+
+    def isSetValue(self):
+        return self.cterm.isSetValue()
+
+    def getSetValue(self):
+        elems = set()
+        for e in self.cterm.getSetValue():
+            term = Term(self.solver)
+            term.cterm = e
+            elems.add(term)
+        return elems
+
+    def isSequenceValue(self):
+        return self.cterm.isSequenceValue()
+
+    def getSequenceValue(self):
+        elems = []
+        for e in self.cterm.getSequenceValue():
+            term = Term(self.solver)
+            term.cterm = e
+            elems.append(term)
+        return elems
+
+    def isUninterpretedValue(self):
+        return self.cterm.isUninterpretedValue()
+
+    def getUninterpretedValue(self):
+        cdef pair[c_Sort, int32_t] p = self.cterm.getUninterpretedValue()
+        cdef Sort sort = Sort(self.solver)
+        sort.csort = p.first
+        i = p.second
+        return (sort, i)
+
+    def isTupleValue(self):
+        return self.cterm.isTupleValue()
+
+    def getTupleValue(self):
+        elems = []
+        for e in self.cterm.getTupleValue():
+            term = Term(self.solver)
+            term.cterm = e
+            elems.append(term)
+        return elems
+
+    def getIntegerValue(self):
+        return int(self.cterm.getIntegerValue().decode())
+
+    def isRealValue(self):
+        return self.cterm.isRealValue()
+
+    def getRealValue(self):
+        """Returns the value of a real term as a Fraction"""
+        return Fraction(self.cterm.getRealValue().decode())
+
+    def isBitVectorValue(self):
+        return self.cterm.isBitVectorValue()
+
+    def getBitVectorValue(self, base = 2):
+        """Returns the value of a bit-vector term as a 0/1 string"""
+        return self.cterm.getBitVectorValue(base).decode()
+
     def toPythonObj(self):
-        '''
+        """
         Converts a constant value Term to a Python object.
 
         Currently supports:
@@ -1610,61 +2695,23 @@ cdef class Term:
           Int     -- returns a Python int
           Real    -- returns a Python Fraction
           BV      -- returns a Python int (treats BV as unsigned)
+          String  -- returns a Python Unicode string
           Array   -- returns a Python dict mapping indices to values
                   -- the constant base is returned as the default value
-          String  -- returns a Python Unicode string
-        '''
-
-        string_repr = self.cterm.toString().decode()
-        assert string_repr
-        sort = self.getSort()
-        res = None
-        if sort.isBoolean():
-            if string_repr == "true":
-                res = True
-            else:
-                assert string_repr == "false"
-                res = False
-
-        elif sort.isInteger():
-            updated_string_repr = string_repr.strip('()').replace(' ', '')
-            try:
-                res = int(updated_string_repr)
-            except:
-                raise ValueError("Failed to convert"
-                                 " {} to an int".format(string_repr))
-
-        elif sort.isReal():
-            updated_string_repr = string_repr
-            try:
-                # rational format (/ a b) most likely
-                # note: a or b could be negated: (- a)
-                splits = [s.strip('()/')
-                          for s in updated_string_repr.strip('()/') \
-                          .replace('(- ', '(-').split()]
-                assert len(splits) == 2
-                num = int(splits[0])
-                den = int(splits[1])
-                res = Fraction(num, den)
-            except:
-                try:
-                    # could be exact: e.g., 1.0
-                    res = Fraction(updated_string_repr)
-                except:
-                    raise ValueError("Failed to convert "
-                                     "{} to a Fraction".format(string_repr))
-
-        elif sort.isBitVector():
-            # expecting format #b<bits>
-            assert string_repr[:2] == "#b"
-            python_bin_repr = "0" + string_repr[1:]
-            try:
-                res = int(python_bin_repr, 2)
-            except:
-                raise ValueError("Failed to convert bitvector "
-                                 "{} to an int".format(string_repr))
-
-        elif sort.isArray():
+        """
+
+        if self.isBooleanValue():
+            return self.getBooleanValue()
+        elif self.isIntegerValue():
+            return self.getIntegerValue()
+        elif self.isRealValue():
+            return self.getRealValue()
+        elif self.isBitVectorValue():
+            return int(self.getBitVectorValue(), 2)
+        elif self.isStringValue():
+            return self.getStringValue()
+        elif self.getSort().isArray():
+            res = None
             keys = []
             values = []
             base_value = None
@@ -1691,33 +2738,7 @@ cdef class Term:
             for k, v in zip(keys, values):
                 res[k] = v
 
-        elif sort.isString():
-            # Strip leading and trailing double quotes and replace double
-            # double quotes by single quotes
-            string_repr = string_repr[1:-1].replace('""', '"')
-
-            # Convert escape sequences
-            res = ''
-            escape_prefix = '\\u{'
-            i = 0
-            while True:
-              prev_i = i
-              i = string_repr.find(escape_prefix, i)
-              if i == -1:
-                res += string_repr[prev_i:]
-                break
-
-              res += string_repr[prev_i:i]
-              val = string_repr[i + len(escape_prefix):string_repr.find('}', i)]
-              res += chr(int(val, 16))
-              i += len(escape_prefix) + len(val) + 1
-        else:
-            raise ValueError("Cannot convert term {}"
-                             " of sort {} to Python object".format(string_repr,
-                                                                   sort))
-
-        assert res is not None
-        return res
+            return res
 
 
 # Generate rounding modes
@@ -1741,3 +2762,30 @@ for rm_int, name in __rounding_modes.items():
 del r
 del rm_int
 del name
+
+
+# Generate unknown explanations
+cdef __unknown_explanations = {
+    <int> REQUIRES_FULL_CHECK: "RequiresFullCheck",
+    <int> INCOMPLETE: "Incomplete",
+    <int> TIMEOUT: "Timeout",
+    <int> RESOURCEOUT: "Resourceout",
+    <int> MEMOUT: "Memout",
+    <int> INTERRUPTED: "Interrupted",
+    <int> NO_STATUS: "NoStatus",
+    <int> UNSUPPORTED: "Unsupported",
+    <int> OTHER: "Other",
+    <int> UNKNOWN_REASON: "UnknownReason"
+}
+
+for ue_int, name in __unknown_explanations.items():
+    u = UnknownExplanation(ue_int)
+
+    if name in dir(mod_ref):
+        raise RuntimeError("Redefinition of Python UnknownExplanation %s."%name)
+
+    setattr(mod_ref, name, u)
+
+del u
+del ue_int
+del name