<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>Relative Correctness: A Bridge Between Testing and Proving</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Nafi Diallo</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Wided Ghardallou</string-name>
          <email>wided.ghardallou@gmail.com</email>
          <xref ref-type="aff" rid="aff1">1</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Ali Mili</string-name>
          <email>ali.mili@njit.edu</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>New Jersey Institute of Technology</institution>
          ,
          <addr-line>Newark, NJ</addr-line>
          ,
          <country country="US">USA</country>
        </aff>
        <aff id="aff1">
          <label>1</label>
          <institution>University of Tunis El Manar</institution>
          ,
          <addr-line>Tunis</addr-line>
          ,
          <country country="TN">Tunisia</country>
        </aff>
      </contrib-group>
      <abstract>
        <p>Relative correctness is the property of a program to be morecorrect than another with respect to a specification. Whereas traditionally we distinguish between two categories of candidate programs, namely correct programs and incorrect programs, relative correctness arranges candidate programs on a partial ordering structure, whose maximal elements are the correct programs. Also, whereas traditionally we deploy proof methods on correct programs to prove their correctness and we deploy testing methods on incorrect programs to detect and remove their faults, relative correctness enables us to bridge this gap by showing that we can deploy static analytical methods to an incorrect program to prove that while it may be incorrect, it is still more-correct than another. We are evolving a technique, called debugging without testing, in which we can remove a fault from a program and prove that the new program is more-correct than the original, all without any testing (and its associated uncertainties/ imperfections). Given that there are orders of magnitude more incorrect programs than correct programs in use nowadays, this has the potential to expand the scope of proving methods significantly. Also, relative correctness has other broad implications for testing and proving, which we briefly explore in this paper.</p>
      </abstract>
      <kwd-group>
        <kwd>absolute correctness</kwd>
        <kwd>relative correctness</kwd>
        <kwd>program testing</kwd>
        <kwd>program proving</kwd>
        <kwd>debugging without testing</kwd>
        <kwd>programming without refining</kwd>
        <kwd>testing for relative correctness</kwd>
      </kwd-group>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>-</title>
      <p>Relative correctness is the property of a program to be more-correct than another
with respect to a given specification; intuitively, a program P ′ is more-correct
than a program P with respect to a specification R if and only if P ′ behaves
according to R more often than P , and violates R less egregiously (i.e. in fewer
ways) than P . Whereas traditionally we distinguish between two categories of
candidate programs for a given specification R, namely correct programs and
incorrect programs, relative correctness enables us to arrange candidate
programs over a partial ordering structure, whose maximal elements are the correct
programs, and all non-maximal elements are incorrect.</p>
      <p>Also, traditionally, proving methods and testing methods have been used on
different sets of programs:
– Proving methods are deployed on correct programs to prove their
correctness; they are of limited use when deployed on incorrect programs because
even when a proof fails, we cannot always conclude that the program is
incorrect, since we cannot tell whether the proof failed because the program is
incorrect or because it was improperly documented (re: invariant assertions,
intermediate assertions, etc). Some methods of program analysis can identify
sources of faults when the correctness proof fails, but their scope is limited.
– Testing methods are deployed on incorrect programs to detect, locate and
remove their faults; they are useless when deployed on correct programs,
because no matter how often a program runs failure-free under test, we can
never (in practice) conclude with certainty that it is correct.</p>
      <p>
        We argue that consideration of relative correctness has the potential to alter the
practice of proving methods and testing methods:
– Once we have a formal definition of relative correctness, we can deploy
proving methods to an incorrect program to prove that while it may be incorrect,
it is still more-correct than another. Given that there are orderes of
magnitude more incorrect programs than correct programs, the ability to apply
proving methods to incorrect programs expands the scope of these methods
significantly. This approach is discussed in section 4.
– Relative Correctness can also alter the practice of software testing by
recognizing the difference between testing for relative correctness and testing
for absolute correctness. When we remove a fault from a program, we ought
to test it for relative correctness rather than absolute correctness, unless we
have reason to believe (how do we ever?) that the fault we have just removed
is the last fault of the program. This matter is discussed in section 5.
– It has long been a cornerstone of software engineering wisdom that programs
should not be developed then checked for correctness, but should instead
be developed hand-in-hand along with their proof, with the proof leading
the way [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ]; echoing David Gries, Carrol Morgan talks about developing
programs by calculation from their specification, in the same way that a
mathematician solves an equation by computing its root [
        <xref ref-type="bibr" rid="ref22">22</xref>
        ]. The prevailing
paradigm for developing programs from specifications is that of refinement,
whereby a program is derived from a specification through a sequence of
correctness-preserving transformations based on refinement. In section 6 we
present an alternative paradigm based on relative correctness, illustrate it
with a simple example, and briefly compare it to related work.
      </p>
      <p>In section 2 we introduce the mathematical background that is needed to carry
out our discussions, and in section 3 we introduce our definition of relative
correctness for deterministic and non-deterministic programs. Also, we conclude in
section 7 by summarizing our findings, discussing related work, and sketching
future directions of research.
2
2.1</p>
    </sec>
    <sec id="sec-2">
      <title>Background</title>
      <sec id="sec-2-1">
        <title>Relational Mathematics</title>
        <p>
          In this paper we use relations to represent specifications and programs, hence
we devote this section to discussing some operations and properties of relations.
We assume the reader familiar with elementary relational algebra [
          <xref ref-type="bibr" rid="ref2">2</xref>
          ], hence this
section is not a tutorial on relations as much as it is an introduction to some
relevant definitions and notations. We represent sets in a program-like notation
by writing variable names and associated data types (sets of values); if we write
S as:
        </p>
        <p>x: X; y: Y;
then we mean to let S be the cartesian product S = X × Y ; elements of S
are usually denoted by s and the X - (resp. Y -) component of s is denoted by
x(s) (resp. y(s)). When no ambiguity arises, we may write x for x(s), and x′
for x(s′), etc. A relation R on set S is a subset of S × S. Special relations on S
include the universal relation L = S × S, the identity relation I = {(s, s)|s ∈ S}
and the empty relation φ = {}. Operations on relations include the set theoretic
operations of union (∪), intersection (∩), difference (\) and complement (R);
they also include the converse of a relation R defined by Rb = {(s, s′)|(s′, s) ∈ R},
the domain of a relation defined by dom(R) = {s|∃s′ : (s, s′) ∈ R}, the range
of a relation defined by rng(R) = dom(Rb), and the product of two relations R
and R′ defined by: R ◦ R′ = {(s, s′)|∃s′′ : (s, s′′) ∈ R ∧ (s′′, s′) ∈ R′}; when no
ambiguity arises, we may write RR′ for R ◦ R .
′</p>
        <p>A relation R is said to be reflexive if and only if I ⊆ R, symmetric if and
only if R = Rb, antisymmetric if R ∩ Rb ⊆ I, asymmetric if and only if R ∩ Rb = φ
and transitive if and only if RR ⊆ R. A relation R is said to be total if and only
if I ⊆ RRb and deterministic if and only if RbR ⊆ I. A relation R is said to be a
vector if and only if RL = R; vectors have the form R = A × S for some subset
A of S; we use them as relational representations of sets. In particular, note that
RL can be written as dom(R) × S; we use it as a relational representation of the
domain of R.
2.2</p>
      </sec>
      <sec id="sec-2-2">
        <title>Program Semantics</title>
        <p>Given a program p on space S written in a C-like notation, we define the function
of p (denoted by P ) as the function that p defines on S, i.e. the set of pairs (s, s′)
such that if program p starts execution in state s it terminates in state s′; we
may, when no ambiguity arises, refer to a program and its function by the same
name, P . Because our discussion of correctness and relative correctness refers to
a notion of refinement, we give here a definition of this property.
Definition 2.1. Given two relations R and R′, we say that R′ refines R (abbrev:
R′ ⊒ R or R ⊑ R′) if and only if RL ∩ R L ∩ (R ∪ R ) = R.
′ ′
Intuitively, this definition means that R′ has a larger domain than R and that
on the domain of R, R′ assigns fewer images to each argument that does R. See
Figure 1. We use refinement to define correctness.</p>
        <p>-: 1 1 XXXXXXz- 2
1 XXXXXXz- 2 2 XXXXXXz- 3
2 XXXXXXz 3 3
3</p>
        <p>- 0
: 0 0 XXXXXXz- 1</p>
        <p>R′</p>
        <p>Fig. 1. R′ ⊒ R
Definition 2.2. A program p on space S is said to be correct with respect to
specification R on S if and only if its function P refines R.</p>
        <p>
          This definition is identical (modulo differences of notation) to traditional
definitions of total correctness [
          <xref ref-type="bibr" rid="ref19 ref22 ref8 ref9">8, 9, 19, 22</xref>
          ].
3
3.1
        </p>
      </sec>
    </sec>
    <sec id="sec-3">
      <title>Relative Correctness</title>
      <sec id="sec-3-1">
        <title>Relative Correctness: Deterministic Programs</title>
        <p>
          Proposition 3.1. Due to [
          <xref ref-type="bibr" rid="ref21">21</xref>
          ]. Given a specification R and a program P ,
program P is correct with respect to R if and only if (R ∩ P )L = RL.
Interpretation: RL, the domain of R, is the set of states on which execution of
candidate programs must produce correct outputs according to R; on the other
hand, (R ∩ P )L is the set of states on which execution of candidate program P
does produce correct outputs according to R. The program P is correct if and
only if these two sets are identical.
        </p>
        <p>
          Definition 3.2. Due to [
          <xref ref-type="bibr" rid="ref20">20</xref>
          ]. Given a specification R and two deterministic
programs P and P ′, we say that P ′ is more-correct (resp. strictly more-correct) than
P with respect to R if and only if (R ∩ P ′)L ⊇ (R ∩ P )L (resp. (R ∩ P ′)L ⊃
(R ∩ P )L).
        </p>
        <p>To contrast relative correctness with the definition of correctness given in
Definition 2.2, we may refer to the latter as absolute correctness. We refer to the
domain of (R ∩ P ) as the competence domain of P with respect to R; for
deterministic programs, to be more-correct simply means to have a larger competence
domain. See Figure 2; note that P ′ is more-correct than P but does not duplicate
the correct behavior of P .</p>
        <p>
          In [
          <xref ref-type="bibr" rid="ref20">20</xref>
          ], we find that relative correctness satisfies the following properties:
        </p>
        <sec id="sec-3-1-1">
          <title>1 XXXXXX:Xz 2</title>
          <p>: 1</p>
        </sec>
        <sec id="sec-3-1-2">
          <title>2 XXXXXX-Xz 3</title>
          <p>3
1 XXXXXXXz 2</p>
        </sec>
        <sec id="sec-3-1-3">
          <title>2 XXXXXXXz 3</title>
          <p>3
1
2
3
: 1
: 2
3
– Ordering Properties. Relative correctness is reflexive and transitive, but not
antisymmetric (i.e. two candidate programs could be equally correct, yet
compute distinct functions). As a very simple example, consider R defined by
R = {(0, 0), (1, 0), (1, 2)} and let P and P ′ be defined by P = {(0, 1), (1, 0)},
P ′ = {(0, 2), (1, 2)}.
– Relative correctness culminates in absolute correctness. A (absolutely)
correct program is more-correct than (or as correct as) any candidate program.
– Relative Correctness Implies Enhanced Reliability. If P ′ is more-correct than
P with respect to R, then it is more reliable than P ; but more-reliable is
not equivalent to more-correct, as the latter is a logical property whereas
the former is a stochastic property.
– Relative Correctness and Refinement. Program P ′ refines program P if and
only if P ′ is more-correct than P with respect to any specification R. We
write this property as:</p>
          <p>P ′ ⊒ P ⇔ (∀R : P ′ ⊒R P ).
3.2</p>
        </sec>
      </sec>
      <sec id="sec-3-2">
        <title>Relative Correctness: Non-Deterministic Programs</title>
        <p>
          In this section we generalize the definition of relative correctness to non-deterministic
programs, as provided by [
          <xref ref-type="bibr" rid="ref3">3</xref>
          ]. One may want to ask why we want to discuss
relative correctness of non-deterministic programs, when the programming language
we use is deterministic. The answer is that we want to reason about the relative
correctness of C-like programs without having to compute their function in all
its detail; for example, if program P manipulates variables x, y and z and
program P ′ is more correct than P with respect to R by virtue of dealing better
than P with variables x and y (more in keeping with R), then we want to reach
that conclusion by focusing exclusively on the behavior of P and P ′ on variables
x and y. But doing so means that we do not need to determine how P and P ′
affect variable z; hence we will deal with non-deterministic representations of P
and P ′. We have the following definition, due to [
          <xref ref-type="bibr" rid="ref3">3</xref>
          ].
        </p>
        <p>Definition 3.3. Given a specification R and two programs P and P ′. We say
that P ′ is more-correct than P with respect to R if and only if: (R ∩ P ′)L ⊇
(R∩P )L and (R∩P )L∩R∩P ′ ⊆ P . Also, we say that P ′ is strictly more-correct
than P with respect to R if and only if at least one of the inequalities in this
definition is strict.
Interpretation: The first clause provides that P ′ has a larger competence domain
than P . To understand the second clause, consider that the left-hand side of this
clause represents the set of (s, s′) pairs such that s is in the competence domain
of P (re: (R ∩ P )L) and s′ is an image of s by P ′ that violates specification R
(re: R ∩ P ′). What this clause is providing is that any such (s, s′) pair is in P ; so
that on the competence domain of P , P ′ does not violate specification R unless
P does (but P may violate R in ways that P ′ does not). In other words: P ′ is
more-correct than P with respect to R if and only if P ′ has a larger competence
domain (first clause), and violates R in fewer ways (second clause). See Figure
3: The competence domain of P is {1, 2} and that of P ′ is {1, 2, 3}. Program P ′
violates R by assigning 1 to 1 and 2 to 2; but P is guilty of the same misdeed,
in addition to also assigning 0 to 2 and 3 to 1, in violation of specification R.
Hence P ′ is more-correct than P with respect to R.</p>
        <p>R</p>
        <p>-: 0
0</p>
        <sec id="sec-3-2-1">
          <title>1 XXXXXX:Xz 2</title>
          <p>: 1</p>
        </sec>
        <sec id="sec-3-2-2">
          <title>2 XXXXXX-Xz 3</title>
          <p>3</p>
          <p>P
0 XXXXXX-Xz 1</p>
          <p>* 0
12 HXXHXXHXXHXXHXXHXXHj-XzXz 23
3
0
1
2
3
Our study of relative correctness came about when we attempted to answer
the question: what is a fault in a program? and when can we say that we have
removed a fault from a program? The first matter we have to consider is that any
definition of a fault must refer to a level of granularity at which we want to isolate
faults. The coarsest level of granularity would be to consider the whole program
as a possible fault, but that is clearly unhelpful as far as diagnosing and removing
faults; more typical levels of granularity include a line of code, a lexeme, an
expression, an assignment statement, elementary programming statements, etc.
We use the term feature to refer to any part of the source code at an appropriate
level of granularity, including non-contiguous parts.</p>
          <p>
            Definition 4.1. Due to [
            <xref ref-type="bibr" rid="ref20">20</xref>
            ]. Given a specification R and a program P , a fault
in program P is any feature f that admits a substitute f ′ such that the program
fPa′ulotbrteaminoevdalfrionmP Pis baypraeiprlaocfifnegatfurewsit(hf,ff′′)issuscthricthtlayt mfoirsea-cofarureltctinthPananPd. PA′
obtained from P by replacing f with f ′ is strictly more-correct than P .
Using this definition, we can in principle remove a fault from a program without
testing it; all we need to do is prove that the new program obtained by replacing
f with f ′ is strictly more-correct than P . But in practice it is very difficult to
use this definition, because it is difficult to compute the functions of P and P ′,
then the competence domains of P and P ′, then compare them.
4.2
          </p>
        </sec>
      </sec>
      <sec id="sec-3-3">
        <title>Proving Correctness and Incorrectness of Loops</title>
        <p>
          The analysis of while loops by means of invariant relations provides a way to infer
the relative correctness of iterative programs from partial semantic information.
In [
          <xref ref-type="bibr" rid="ref17">17</xref>
          ] we discuss how to use invariant relations to prove the correctness or
incorrectness of an iterative program of the form w: while (t) {b;}. If we let B be
the function of the loop body ({b;}) and we let T be the vector that represents
the loop condition (i.e. T = {(s, s′)|t(s)}) then an invariant relation of the while
loop w is a reflexive transitive superset of (T ∩B). We are evolving a tool that can
generate invariant relations for common combinations of code patterns; this tool
matches an internal representation of the loop against prestored code patterns
(called the recognizers) for which it has patterns of invariant relations; actual
invariant relations are generated by instantiating the patterns of invariant relations
with actual program variables. The following table shows, for the sake of
illustration, two sample recognizers dealing with numeric variables; more information
on this tool can be found at https://selab.njit.edu/tools/fxloops.php.
        </p>
        <p>ID Data</p>
        <p>int x;
1R1 const int a&gt;0; x=x+a</p>
        <p>float x, y; x=x+a,
2R1 const float a, b; y=y+b</p>
        <p>Statements Invariant Relation Pattern
{(s, s′)|x mod a = x′ mod a}
{(s, s′)|ay − bx = ay′ − bx′}.</p>
        <p>
          Invariant relations are important for our purposes because they enable us to
determine whether a loop is correct or not with respect to a specification, as
shown in the following two propositions (which are due to [
          <xref ref-type="bibr" rid="ref17">17</xref>
          ]). Even though
correctness is defined in terms of the program function, invariant relations enable
us to rule on correctness or incorrectness long before we have collected all the
necessary information to compute the loop function.
        </p>
        <p>Proposition 4.2. Necessary condition of correctness. Let w be a while loop of
the form w = while (t) {b} that terminates for all states in S, let V be an
invariant relation for w, and let R be a specification on S. If w is correct with
respect to R then (R ∩ V )T = RL.</p>
        <p>This proposition provides, in effect, that any while loop that admits an invariant
relation V that fails to meet this condition could not possibly be correct with
respect to R. We say about such an invariant relation V that it is incompatible
with the specification R. Any invariant relation that is not incompatible with
the specification is said to be compatible.
Proposition 4.3. Sufficient condition of correctness. Given a while loop w of
the form while (t) {b} that terminates for all states in its space S, and given
a specification R on S, if an invariant relation V of w satisfies the condition</p>
        <p>V T ∩ RL ∩ (R ∪ V ∩ Tb) = R
then w is correct with respect to R.</p>
        <p>This proposition provides, in effect, that if an invariant relation V meets this
condition, then it contains sufficient information to subsume the specification,
and to prove the correctness of the loop with respect to R.
4.3</p>
      </sec>
      <sec id="sec-3-4">
        <title>Proving Relative Correctness of Loops</title>
        <p>
          While in the previous section we show how invariant relations can be used to
prove the (absolute) correctness or incorrectness of a loop with respect to a
specification, in this section we explore how they can be used to prove relative
correctness of a loop over another with respect to a given specification. A
proposition given in [
          <xref ref-type="bibr" rid="ref6">6</xref>
          ] provides an intuitive result to the effect that if we alter a while
loop in such a way as to migrate an invariant relation from the incompatible
column to the compatible column, while preserving all the compatible invariant
relations, we obtain a while loop that is strictly more-correct; in other words,
whatever alteration we make to the while loop as described above can be
considered as a monotonic fault removal. This proposition can be used to diagnose
and remove faults in a while loop:
– Evidence that a fault exists. The first step in fault removal is to have evidence
that a fault does indeed exist. In our case, the existence of an incompatible
invariant relation proves that the program has a fault.
– Locating the fault. Among all the incompatible invariant relations, we select
that which involves the smallest number of program variables; this enables
us to focus our attention on those statements of the program that involve
the variables in question.
– Removing the fault. We must modify the selected variable(s), while ensuring
that all the compatible invariant relations are preserved. The constraint of
preserving the compatible invariant relations is used for guidance in deciding
how to change the selected variables.
– Verifying that the fault has been removed. For each modification that is
generated in the previous step, we deploy the invariant relations generator and
check whether the incompatible invariant relation identified in the second
step has been replaced by a compatible invariant relation. Then we are sure
that the new loop is strictly more-correct than the original loop (and the
fault has been removed).
        </p>
        <p>
          As an illustration of this approach, we consider the following example, which
we borrow from [
          <xref ref-type="bibr" rid="ref7">7</xref>
          ]. The space of the specification is defined by the following
variable declarations: char q[]; int let, dig, other, i, l; char c;.
The specification R that we use for relative correctness is:
R = {(s, s′)|q ∈ listhαA ∪ αa ∪ ϑ ∪ σi ∧ let′ = let + #a(q) + #A(q) ∧ dig′ =
dig + #ϑ(q) ∧ other′ = other + #σ(q)}
where listhT i denotes the set of lists of elements of type T , #A, #a, #ϑ and
#σ the functions that to each list q assign (respectively) the number of upper
case alphabetic characters, lower case alphabetic characters, numeric digits and
symbols. The (faulty) program that we consider is w:
{i=0; let=0; dig=0; other=0; l=strlen(q);
while (i&lt;l) {c = q[i];i++;
if (A&lt;=c &amp;&amp; Z&gt;=c) let=let-1;
else if (a&lt;=c &amp;&amp; z&gt;=c) let=let-1;
else if (0&gt;c &amp;&amp; 9&gt;=c) dig=dig+1;
        </p>
        <p>else other=other+1;}}
We find the following invariant relations of this while loop, where σ1 and σ2
designate the set of characters whose Ascii codes are less than, and (respectively)
greater than equal to, the code of ’0’:
– V0 = {(s, s′)|q = q′}
– V1 = {(s, s′)|i ≤ i′}
– V2 = {(s, s′)|dig ≤ dig′}
– V3 = {(s, s′)|other ≤ other′}
– V4 = {(s, s′)|let ≥ let′}
– V5 = {(s, s′)|let − #a∪A(q[i..l − 1]) = let′ − #a∪A(q′[i′..l − 1])}
– V6 = {(s, s′)|dig + #σ1(q[i..l − 1]) = dig′ + #σ1(q′[i′..l − 1])}
– V7 = {(s, s′)|other + #σ2∪ϑ(q[i..l − 1]) = other′ + #σ2∪ϑ(q′[i′..l − 1])}
The following table shows which of these invariant relations are compatible, and
which are incompatible.</p>
        <p>Compatible Invariant Relations Incompatible Invariant Relations
V0, V1, V2, V3 V4, V5, V6, V7
Becuse the incompatible column is non-empty, we conclude that the program is
incorrect with respect to R, hence we must enhance its correctness. To this effect,
we select the incompatible invariant relation V4 for remediation, which leads us
to focus on variable let for fault removal. Preservation of the compatible invariant
relations mandates that let be modified under the following condition: let ≤ let′.
We propose: let=let+1;. The generation of invariant relations of the new loop
yields the following table:</p>
        <p>Compatible Invariant Relations Incompatible Invariant Relations
V0, V1, V2, V3, V4′, V5′ V6, V7
Application of the same process one more time yields the following program:
{i=0; let=0; dig=0; other=0; l=strlen(q);
while (i&lt;l) {c = q[i];i++;
if (A&lt;=c &amp;&amp; Z&gt;=c) let=let+1;
else if (a&lt;=c &amp;&amp; z&gt;=c) let=let+1;
else if (0&lt;=c &amp;&amp; 9&gt;=c) dig=dig+1;
else other=other+1;}}
Analysis of this program produces 8 invariant relations, which are all compatible.</p>
        <p>Compatible Invariant Relations Incompatible Invariant Relations
V0, V1, V2, V3, V4′, V5′, V6′, V7′
This does not prove that the program is now correct, all it proves is that we
have no evidence (in the forms of an incompatible invariant relation) that it is
incorrect. To establish correctness, we must ensure that the intersection of all
the compatible invariant relations satisfies the sufficient condition provided by
Proposition 4.3, which it does. All the faults have been removed; we now have a
correct program.
5</p>
      </sec>
    </sec>
    <sec id="sec-4">
      <title>Testing for Relative Correctness</title>
      <p>The usual process of software debugging proceeds as follows: We observe a failure
of the program; we analyze the failure and formulate a hypothesis on its cause;
we modify the source code on the basis of our hypothesis; and finally we test
the new program to ensure that it is now correct. But there is a serious flaw
in this process: when we remove a fault from an incorrect program, we have
no reason to expect the new program to be correct, unless we know (how do
we ever?) that the fault we have just removed is the last fault of the program;
hence when a fault is removed from a program, the new program ought to be
tested for relative correctness over the original program, rather than for absolute
correctness. Of course, regression testing is supposed to ensure monotonicity of
fault removal, but regression testing is essentially a test data selection matter,
whereas the difference between testing for relative correctness and testing for
absolute correctness involves other aspects. We argue that testing a program for
relative correctness has an impact on three aspects of testing, namely test data
selection, test oracle design, and test coverage assessment.</p>
      <p>
        – Test data selection. The problem of test data selection can be summarized
as follows: We are given a large or infinite test space S, and we must select
a small subset thereof T such that the behavior of candidate programs on
T is a faithful predictor of their behavior on S. The difference between
absolute correctness and relative correctness is that for absolute correctness
with respect to specification R, the test space S is dom(R) whereas for
relative correctness over P with respect to R the test space S is dom(R ∩ P ).
– Test oracle design. Let Ω(s, s′) be the test oracle for absolute correctness
derived from specification R. Because relative correctness over program P
tests a candidate program P ′ for Ω only for those states on which P is
successful, the oracle for relative correctness ω(s, s′) can be written as:
ω(s, s′) ≡ (Ω(s, P (s)) ⇒ Ω(s, s′)).
– Test Coverage Assessment. It is not sufficient to know that some program
P ′ has executed successfully on a test data set of size N using oracle ω(s, s′);
it is also necessary to know what percentage of the test data set satisfy the
precondition Ω(s, P (s)) (i.e. is P ′ more-correct than P because P ′ is very
good or because P is very bad?).
To illustrate the difference between absolute correctness and relative correctness,
we consider the same program as section 4, and we resolve to remove its faults
not by static analysis, as we did there, but by testing for relative correctness after
each fault removal. To this effect, we proceed iteratively as follows, starting from
the original program:
1. Using muJava [
        <xref ref-type="bibr" rid="ref18">18</xref>
        ], we generate mutants of the program, and submit each
mutant to three tests:
– A test for absolute correctness, using oracle Ω(s, s′) derived from
specification R.
– A test for relative correctness, using oracle ω(s, s′) derived from Ω(s, s′).
– A test for strict relative correctness, which in addition to relative
correctness also ensures that there is at least one state on which the mutant
satisfies Ω whereas the base program fails it.
2. We select those mutants which prove to be strictly more-correct than the
base program, make each one of them a base program on which we apply
recursively the same procedure, starting from step 1 above.
      </p>
      <p>We invoke muJava with the option of mutating statements and conditions and we
test every mutant for relative correctness, strict relative correctness and absolute
correctness using randomly generated test data of size 1000. Every invokation
of muJava generates exactly 64 mutants, which we label by indices 1 through
64; hence for example m4.53.8 is mutant 8 of mutant 53 of mutant 4 of the
original program. The outcome of this experiment is shown in Figure 4. The
arcs represent relative correctness relationships; at the bottom of this graph
is the original program, and at the top is the corrected version of the program.
Note that the test for absolute correctness kept coming empty-handed every time
except whenever muJava produced the correct program P ′. i.e. six times. The
test for relative correctness returned true for every arc in Figure 4 i.e. 25 times;
it enabled us to remove faults one at a time, until we reach a correct program.
Note also that many mutations prove to be perfectly commutative; such is the
case for 4, 8 and 53. Note further that, if we assume for the sake of argument
that our test is exhaustive, then the number of arcs emerging from each program
represents the number of faults in that program. For example, program P has
four faults even though it is three fault removals away from being correct; we
say that P has a fault density of 4 and a fault depth of 3.
6</p>
    </sec>
    <sec id="sec-5">
      <title>Programming Without Refinement</title>
      <p>
        For all its interest, program verification is really a dubious bargain: the idea that
we write programs in an informal/ approximate manner then try to prove their
correctness is not very sound. What is more rational is to seek means to write
programs that are certified to be correct by construction [
        <xref ref-type="bibr" rid="ref22 ref23 ref8 ref9">8, 9, 22, 23</xref>
        ]. Given a
specification, we can do so in one of two ways:
m51.4.53
      </p>
      <p>m51.8.53
= m51.53.4</p>
      <p>= m51.53.8
m4 Qk</p>
      <p>Q</p>
      <p>Q</p>
      <p>Q
m8
Q</p>
      <p>KA
A</p>
      <p>A
Q A</p>
      <p>QA</p>
      <p>
        P
– Preserve correctness, achieve program quality. Starting from the
specification, we transform it progressively to make it more and more program-like,
while preserving correctness through refinement. This is the traditional
process of program construction by successive refinements.
– Preserve program quality, achieve correctness. Starting from the trivial
program abort, we transform it progressively to make more and more-correct
with respect to R until it is correct. In [
        <xref ref-type="bibr" rid="ref5">5</xref>
        ], we present this model as an
alternative program derivation paradigm, and discuss some of its advantages;
we refer to this approach as programming without refinement.
      </p>
      <p>We illustrate this process on a program that computes the number of alphabetic
characters, numeric characters, and special symbols in an input string. We
consider the same space and relation as those given in Section 4, and we present a
sequence of programs P0 ... P4 that are increasingly more-correct, though not
increasingly more-refined. We do not have a methodology for how to do this in
general, hence we merely present the programs and their competence domains.
P0 {abort}. CD0 = φ.</p>
      <p>P1 {i=0; let=0; dig=0; other=0; l=strlen(q);
while (i&lt;l) { c = q[i]; i++;</p>
      <p>if (’A’&lt;=c &amp;&amp; ’Z’&gt;=c) let+=1;}}</p>
      <p>CD2 = {s|q ∈ listhαAi}.</p>
      <p>P2 {i=0; let=0; dig=0; other=0; l=strlen(q);
while (i&lt;l) {c = q[i]; i++;
if (’A’&lt;=c &amp;&amp; ’Z’&gt;=c) let+=1;
else if (’a’&lt;=c &amp;&amp; ’z’&gt;=c) let+=1;}}</p>
      <p>CD2 = {s|q ∈ listhαA ∪ αai}.</p>
      <p>P3 {i=0; let=0; dig=0; other=0; l=strlen(q);
while (i&lt;l) {c = q[i]; i++;
if (’A’&lt;=c &amp;&amp; ’Z’&gt;=c) let+=1;
else if (’a’&lt;=c &amp;&amp; ’z’&gt;=c) let+=1;
else if (’0’&lt;=c &amp;&amp; ’9’&gt;=c) dig+=1;}}</p>
      <p>CD3 = {s|q ∈ listhαA ∪ αa ∪ νi}.</p>
      <p>P4 {i=0; let=0; dig=0; other=0; l=strlen(q);
while (i&lt;l) {c = q[i]; i++;
if (’A’&lt;=c &amp;&amp; ’Z’&gt;=c) let+=1;
else if (’a’&lt;=c &amp;&amp; ’z’&gt;=c) let+=1;
else if (’0’&lt;=c &amp;&amp; ’9’&gt;=c) dig+=1;
else other+=1;}</p>
      <p>CD4 = {s|q ∈ listhαA ∪ αa ∪ ν ∪ σi}.</p>
      <p>Clearly, we do have P0 ⊑R P1 ⊑R P2 ⊑R P3 ⊑R P4; also, we find CD4 =
dom(R), hence (by Proposition 3.1) P4 ⊒ R, i.e. P4 is correct with respect to R.</p>
      <p>
        This process bears a striking resemblance to test driven derivation (TDD)
[
        <xref ref-type="bibr" rid="ref10 ref24">10, 24</xref>
        ], in the following sense: if we let Q1, Q2, Q3, ... Qn be the successive test
data samples (in the form of (input, output) pairs) used in successive iterations
of TDD, and if we let Ri be defined as Ri = Sik=1 Qi, then we argue that TDD
is nothing but an instance of programming without refinement, where the
specification is R = Rn and the successive competence domains are CDi = dom(Ri).
Clearly, the competence domains are increasingly large, by construction of Ri,
hence we do have P1 ⊑R P2 ⊑R P3 ⊑R ... ⊑R Pn−1 ⊑R Pn, where Pi is the
program obtained after considering the ith test data sample Qi.
7
      </p>
    </sec>
    <sec id="sec-6">
      <title>Concluding Remarks</title>
      <p>
        In [
        <xref ref-type="bibr" rid="ref1 ref12 ref13 ref14">1, 12–14</xref>
        ] Laprie et al. discuss various aspects of dependability, and argue
that faults are at the center of the study of dependability;and yet, they provide
a rather vague definition of a fault: A fault is the adjudged or hypothesized cause
of an error [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ]. In [
        <xref ref-type="bibr" rid="ref20 ref3 ref4">3, 4, 20</xref>
        ] we attempt to give a formal definition of a fault, and
find that a way (the only way?) to do so is to introduce the concept of relative
correctness. Beyond defining faults, this concept has many implications, of which
we discuss three in this paper, as they pertain to program proving and program
testing.
      </p>
      <p>
        – Impact on Proving. Whereas traditionally proving methods are deployed
exclusively on correct programs to prove their correctness, we argue that
relative correctness enables us to apply proving methods to an incorrect program
to prove that despite being incorrect, it is still more correct than another.
Given that there are orders of magnitude more incorrect programs than
correct programs, this points to a potential expansion of the scope of static
analysis methods.
– Impact on Testing. We argue that when we remove a fault from a program
we ought to test it for relative correctness rather than absolute correctness;
of course regression testing is an attempt to test for relative correctness
through the selection of targeted test data, but we argue that testing for
relative correctness has an impact not only on test data selection, but also
on oracle design and test coverage assessment.
– Impact on Program Derivation. Whereas the traditional paradigm of
program derivation is to proceed by successive correctness-preserving
transformations on the basis of refinement, we argue that it is also possible to proceed
by successive correctness-enhancing transformations on the basis of relative
correctness. One of the main advantages of our paradigm is that it models
not only the derivation of programs from scratch, but also many aspects of
software evolution. Given that more software is produced by evolution than
from scratch, this approach carries significant potential in practice.
Other authors have introduced similar-sounding but distinct concepts of relative
correctness [
        <xref ref-type="bibr" rid="ref11 ref15 ref16">11, 15, 16</xref>
        ] in the context of software testing and program repair.
Their work differs from ours in terms of its specification format (executable
assertions, vs relations), its program semantics (execution traces, vs. program
functions), its definition of correctness (all assertions are true, vs refinement),
its definition of relative correctness (more valid traces, fewer invalid traces vs
larger competence domain and fewer violations), and its goals (fault removal, vs
proving, testing, derivation and evolution).
      </p>
      <p>Whereas most other authors approximate while loops by unrolling them a
number of times, we approximate them by means of invariant relations. We view
the contrast between unrolling a loop and capturing its behavior by invariant
relations as a choice between capturing all the functional details of a few
iterations, and capturing some functional detail of all the iterations. We argue that
capturing all the functional details is typically unnecessary (not all the loop’s
functional properties are worthy / relevant), and modeling a limited number of
iterations is typically insufficient (the behavior of the loop for a limited number
of iterations may not indicate its behavior for an arbitrary number thereof).
Acknowledgement The authors are very grateful to the anonymous reviewers
for their thoughful, valuable feedback.</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          [1]
          <string-name>
            <given-names>Algirdas</given-names>
            <surname>Avizienis</surname>
          </string-name>
          , Jean Claude Laprie, Brian Randell, and Carl E Landwehr.
          <article-title>Basic concepts and taxonomy of dependable and secure computing</article-title>
          .
          <source>IEEE Transactions on Dependable and Secure Computing</source>
          ,
          <volume>1</volume>
          (
          <issue>1</issue>
          ):
          <fpage>11</fpage>
          -
          <lpage>33</lpage>
          ,
          <year>2004</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          [2]
          <string-name>
            <given-names>Chris</given-names>
            <surname>Brink</surname>
          </string-name>
          , Wolfram Kahl, and
          <string-name>
            <given-names>Gunther</given-names>
            <surname>Schmidt</surname>
          </string-name>
          .
          <source>Relational Methods in Computer Science. Advances in Computer Science</source>
          . Springer Verlag, Berlin, Germany,
          <year>1997</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          [3]
          <string-name>
            <given-names>Jules</given-names>
            <surname>Desharnais</surname>
          </string-name>
          , Nafi Diallo, Wided Ghardallou, Marcelo Frias, Ali Jaoua, and Ali Mili.
          <article-title>Mathematics for relative correctness</article-title>
          .
          <source>In Relational and Algebraic Methods in Computer Science</source>
          ,
          <year>2015</year>
          , pages
          <fpage>191</fpage>
          -
          <lpage>208</lpage>
          , Lisbon, Portugal,
          <year>September 2015</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          [4]
          <string-name>
            <given-names>Nafi</given-names>
            <surname>Diallo</surname>
          </string-name>
          , Wided Ghardallou, and
          <string-name>
            <given-names>Ali</given-names>
            <surname>Mili</surname>
          </string-name>
          .
          <article-title>Correctness and relative correctness</article-title>
          .
          <source>In Proceedings, 37th International Conference on Software Engineering</source>
          , Firenze, Italy, May
          <volume>20</volume>
          -22
          <year>2015</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          [5]
          <string-name>
            <given-names>Nafi</given-names>
            <surname>Diallo</surname>
          </string-name>
          , Wided Ghardallou, and
          <string-name>
            <given-names>Ali</given-names>
            <surname>Mili</surname>
          </string-name>
          .
          <article-title>Program derivation by correctness enhancements</article-title>
          .
          <source>In Proceedings, Refinement</source>
          <year>2015</year>
          , Oslo, Norway,
          <year>June 2015</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          [6]
          <string-name>
            <given-names>Wided</given-names>
            <surname>Ghardallou</surname>
          </string-name>
          , Nafi Diallo, Ali Mili, and
          <string-name>
            <given-names>Marcelo</given-names>
            <surname>Frias</surname>
          </string-name>
          .
          <article-title>Debugging without testing</article-title>
          .
          <source>In Proceedings, International Conference on Software Testing</source>
          , Chicago, IL,
          <year>April 2016</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          [7]
          <string-name>
            <given-names>Alberto</given-names>
            <surname>Gonzalez-Sanchez</surname>
          </string-name>
          , Rui Abreu, Hans Gerhart Gross, and
          <string-name>
            <surname>Arjan J.C. van Gemund</surname>
          </string-name>
          .
          <article-title>Prioritizing tests for fault localization through ambiguity group reduction</article-title>
          .
          <source>In proceedings, Automated Software Engineering</source>
          , Lawrence,
          <string-name>
            <surname>KS</surname>
          </string-name>
          ,
          <year>2011</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          [8]
          <string-name>
            <given-names>David</given-names>
            <surname>Gries</surname>
          </string-name>
          .
          <source>The Science of programming</source>
          . Springer Verlag,
          <year>1981</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref9">
        <mixed-citation>
          [9]
          <string-name>
            <surname>Eric</surname>
            <given-names>C.R.</given-names>
          </string-name>
          <string-name>
            <surname>Hehner</surname>
          </string-name>
          .
          <article-title>A Practical Theory of Programming</article-title>
          .
          <source>Prentice Hall</source>
          ,
          <year>1992</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref10">
        <mixed-citation>
          [10]
          <string-name>
            <given-names>David</given-names>
            <surname>Janzen</surname>
          </string-name>
          and
          <string-name>
            <given-names>Hossein</given-names>
            <surname>Saiedian</surname>
          </string-name>
          .
          <article-title>Test driven development: Concepts, taxonomy and future direction</article-title>
          .
          <source>IEEE Computer</source>
          ,
          <volume>38</volume>
          (
          <issue>9</issue>
          ):
          <fpage>43</fpage>
          -
          <lpage>50</lpage>
          ,
          <year>September 2005</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref11">
        <mixed-citation>
          [11]
          <string-name>
            <surname>Shuvendu</surname>
            <given-names>K.</given-names>
          </string-name>
          <string-name>
            <surname>Lahiri</surname>
          </string-name>
          ,
          <string-name>
            <surname>Kenneth L. McMillan</surname>
          </string-name>
          ,
          <string-name>
            <surname>Rahul Sharma</surname>
          </string-name>
          , and Chris Hawblitzel.
          <article-title>Differential assertion checking</article-title>
          .
          <source>In Proceedings, ESEC/ SIGSOFT FSE</source>
          , pages
          <fpage>345</fpage>
          -
          <lpage>455</lpage>
          ,
          <year>2013</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref12">
        <mixed-citation>
          [12]
          <string-name>
            <surname>Jean</surname>
            <given-names>Claude</given-names>
          </string-name>
          <string-name>
            <surname>Laprie</surname>
          </string-name>
          .
          <source>Dependability: Basic Concepts</source>
          and Terminology: in English, French, German, Italian and Japanese. Springer Verlag, Heidelberg,
          <year>1991</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref13">
        <mixed-citation>
          [13]
          <string-name>
            <surname>Jean</surname>
            <given-names>Claude</given-names>
          </string-name>
          <string-name>
            <surname>Laprie</surname>
          </string-name>
          . Dependability
          <article-title>-its attributes, impairments and means</article-title>
          .
          <source>In Predictably Dependable Computing Systems</source>
          , pages
          <fpage>1</fpage>
          -
          <lpage>19</lpage>
          . Springer Verlag,
          <year>1995</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref14">
        <mixed-citation>
          [14]
          <string-name>
            <surname>Jean</surname>
            <given-names>Claude</given-names>
          </string-name>
          <string-name>
            <surname>Laprie</surname>
          </string-name>
          .
          <article-title>Dependable computing: Concepts, challenges, directions</article-title>
          .
          <source>In Proceedings, COMPSAC</source>
          ,
          <year>2004</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref15">
        <mixed-citation>
          [15]
          <string-name>
            <given-names>Francesco</given-names>
            <surname>Logozzo</surname>
          </string-name>
          and
          <string-name>
            <given-names>Thomas</given-names>
            <surname>Ball</surname>
          </string-name>
          .
          <article-title>Modular and verified automatic program repair</article-title>
          .
          <source>In Proceedings, OOPSLA</source>
          , pages
          <fpage>133</fpage>
          -
          <lpage>146</lpage>
          ,
          <year>2012</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref16">
        <mixed-citation>
          [16]
          <string-name>
            <surname>Francesco</surname>
            <given-names>Logozzo</given-names>
          </string-name>
          , Shuvendu Lahiri, Manual Faehndrich, and
          <string-name>
            <given-names>Sam</given-names>
            <surname>Blackshear</surname>
          </string-name>
          .
          <article-title>Verification modulo versions: Towards usable verification</article-title>
          .
          <source>In Proceedings, PLDI, page 32</source>
          ,
          <year>2014</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref17">
        <mixed-citation>
          [17]
          <string-name>
            <surname>Asma</surname>
            <given-names>Louhichi</given-names>
          </string-name>
          , Wided Ghardallou, Khaled Bsaies, Lamia Labed Jilani, Olfa Mraihi, and
          <string-name>
            <given-names>Ali</given-names>
            <surname>Mili</surname>
          </string-name>
          .
          <article-title>Verifying loops with invariant relations</article-title>
          .
          <source>International Journal of Critical Computer Based Systems</source>
          ,
          <volume>5</volume>
          (
          <issue>1</issue>
          /2):
          <fpage>78</fpage>
          -
          <lpage>102</lpage>
          ,
          <year>2014</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref18">
        <mixed-citation>
          [18]
          <string-name>
            <given-names>Yu</given-names>
            <surname>Seung</surname>
          </string-name>
          <string-name>
            <surname>Ma</surname>
          </string-name>
          , Jeff Offutt, and Yong Rae Kwon.
          <article-title>Mujava: An automated class mutation system</article-title>
          .
          <source>Software Testing, Verification and Reliability</source>
          ,
          <volume>15</volume>
          (
          <issue>2</issue>
          ):
          <fpage>97</fpage>
          -
          <lpage>133</lpage>
          ,
          <year>June 2005</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref19">
        <mixed-citation>
          [19]
          <string-name>
            <given-names>Zohar</given-names>
            <surname>Manna</surname>
          </string-name>
          .
          <source>A Mathematical Theory of Computation</source>
          .
          <source>McGraw Hill</source>
          ,
          <year>1974</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref20">
        <mixed-citation>
          [20]
          <string-name>
            <surname>Ali</surname>
            <given-names>Mili</given-names>
          </string-name>
          , Marcelo Frias, and
          <string-name>
            <given-names>Ali</given-names>
            <surname>Jaoua</surname>
          </string-name>
          .
          <article-title>On faults and faulty programs</article-title>
          . In Peter Hoefner,
          <string-name>
            <given-names>Peter</given-names>
            <surname>Jipsen</surname>
          </string-name>
          , Wolfram Kahl, and Martin Eric Mueller, editors,
          <source>Proceedings, RAMICS: 14th International Conference on Relational and Algebraic Methods in Computer Science</source>
          , volume
          <volume>8428</volume>
          of Lecture Notes in Computer Science, pages
          <fpage>191</fpage>
          -
          <lpage>207</lpage>
          , Marienstatt, Germany, April 28-May
          <year>1st</year>
          2014. Springer.
        </mixed-citation>
      </ref>
      <ref id="ref21">
        <mixed-citation>
          [21]
          <string-name>
            <surname>Harlan</surname>
            <given-names>D.</given-names>
          </string-name>
          <string-name>
            <surname>Mills</surname>
          </string-name>
          ,
          <string-name>
            <surname>Victor R. Basili</surname>
            ,
            <given-names>John D.</given-names>
          </string-name>
          <string-name>
            <surname>Gannon</surname>
          </string-name>
          , and
          <string-name>
            <surname>Dick</surname>
            <given-names>R.</given-names>
          </string-name>
          <string-name>
            <surname>Hamlet</surname>
          </string-name>
          .
          <article-title>Structured Programming: A Mathematical Approach</article-title>
          . Allyn and Bacon, Boston, Ma,
          <year>1986</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref22">
        <mixed-citation>
          [22]
          <string-name>
            <surname>Carrol</surname>
            <given-names>C.</given-names>
          </string-name>
          <string-name>
            <surname>Morgan</surname>
          </string-name>
          . Programming from Specifications.
          <source>International Series in Computer Sciences. Prentice Hall</source>
          , London, UK,
          <year>1998</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref23">
        <mixed-citation>
          [23]
          <string-name>
            <surname>Jose</surname>
            <given-names>N.</given-names>
          </string-name>
          <string-name>
            <surname>Oliveira</surname>
          </string-name>
          .
          <article-title>Programming from metaphorisms</article-title>
          .
          <source>Journal of Logic and Algebraic Programming</source>
          ,
          <year>2016</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref24">
        <mixed-citation>
          [24]
          <string-name>
            <given-names>Daniel</given-names>
            <surname>Perelman</surname>
          </string-name>
          , Sumit Gulwani, Dan Grossman, and
          <string-name>
            <given-names>Peter</given-names>
            <surname>Provost</surname>
          </string-name>
          .
          <article-title>Test driven synthesis</article-title>
          .
          <source>In Proceedings, 35th ACM SIGPLAN Conference</source>
          , PLDI, volume
          <volume>49</volume>
          , pages
          <fpage>408</fpage>
          -
          <lpage>418</lpage>
          , Edinburgh, UK,
          <year>2014</year>
          .
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>