//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
/**
* This is the famous Bridge-crossing puzzle. The aim is to see what the minimum
* amount of time is for 4 people to cross with flash-light (torch): 10.5.2.1.
* The answer is 17 for the given configuration.
*
* One can find this solution with JPF in BFS search mode - DFS will not
* work since this is an infinite-state program (time keeps increasing).
*
* If one uses the ignoreIf(cond) call the branches that will lead to
* solutions worst than the current will be cut-off and this will allow
* DFS also to complete - and BFS to terminate faster.
*
* When setting the native flag in main one can also save information
* from one run to the next using JPF's native peer methods -
* see JPF_Crossing.java for the code of getTotal() and setTotal(int).
*/
package gov.nasa.jpf.test.mc.data;
import org.junit.Test;
import gov.nasa.jpf.util.test.TestJPF;
import gov.nasa.jpf.vm.Verify;
public class CrossingTest extends TestJPF {
static class Constants {
public static final boolean east = true;
public static final boolean west = false;
}
static class Torch {
public static boolean side = Constants.east;
public String toString() {
if (side == Constants.east) {
return "east";
} else {
return "west";
}
}
}
static class Bridge {
static Person[] onBridge = new Person[2];
static int numOnBridge = 0;
public static boolean isFull() {
return numOnBridge != 0;
}
public static int Cross() {
int time = 0;
Torch.side = !Torch.side;
if (numOnBridge == 1) {
onBridge[0].side = Torch.side;
time = onBridge[0].time;
//System.out.println("Person " + onBridge[0] +
// " moved to " + Torch.side +
// " in time " + time);
} else {
assert onBridge[0] != null : "Argh, null " + numOnBridge;
assert onBridge[1] != null;
onBridge[0].side = Torch.side;
onBridge[1].side = Torch.side;
if (onBridge[0].time > onBridge[1].time) {
time = onBridge[0].time;
} else {
time = onBridge[1].time;
}
//System.out.println("Person " + onBridge[0] +
// " and Person " + onBridge[1] +
// " moved to " + Torch.side +
// " in time " + time);
}
return time;
}
public static void clearBridge() {
if (numOnBridge == 0) {
return;
} else if (numOnBridge == 1) {
onBridge[0] = null;
numOnBridge = 0;
} else {
onBridge[0] = null;
onBridge[1] = null;
numOnBridge = 0;
}
}
public static void initBridge() {
onBridge[0] = null;
onBridge[1] = null;
numOnBridge = 0;
}
public static boolean tryToCross(Person th) {
if ((numOnBridge < 2) && (onBridge[0] != th) && (onBridge[1] != th)) {
onBridge[numOnBridge++] = th;
return true;
} else {
return false;
}
}
}
static class Person {
// person to cross the bridge
int id;
public int time;
public boolean side;
public Person(int i, int t) {
time = t;
side = Constants.east;
id = i;
}
public void move() {
if (side == Torch.side) {
if (!Verify.randomBool()) {
Bridge.tryToCross(this);
}
}
}
public String toString() {
return "" + id;
}
}
public static native void setTotal(int time);
// due to these natives the code only runs in JPF
public static native int getTotal();
public void run() {
//Verify.beginAtomic();
// when natives are used one can record the minimal value found so
// far to get a better one on the next execution - this
// requires the -mulitple-errors option and then the last error
// found must be replayed
boolean isNative = false;
if (isNative) {
setTotal(30); // set to value larger than solution (17)
}
int total = 0;
boolean finished = false;
Bridge.initBridge();
Person p1 = new Person(1, 1);
Person p2 = new Person(2, 2);
Person p3 = new Person(3, 5);
Person p4 = new Person(4, 10);
//Verify.endAtomic();
while (!finished) {
//Verify.beginAtomic();
p1.move();
p2.move();
p3.move();
p4.move();
if (Bridge.isFull()) {
total += Bridge.Cross();
if (isNative) {
Verify.ignoreIf(total > getTotal());
} else {
Verify.ignoreIf(total > 17); //with this DFS will also find error
}
Bridge.clearBridge();
//printConfig(p1,p2,p3,p4,total);
finished = !(p1.side || p2.side || p3.side || p4.side);
}
//Verify.endAtomic();
}
//Verify.beginAtomic();
if (isNative) {
if (total < getTotal()) {
System.out.println("new total " + total);
setTotal(total);
assert (total > getTotal());
}
} else {
System.out.println("total time = " + total);
assert (total > 17) : "total > 17 | total = " + total;
}
//Verify.endAtomic();
}
static void printConfig(Person p1, Person p2, Person p3, Person p4,
int total) {
if (p1.side == Constants.east) {
System.out.print("p1(" + p1.time + ")");
}
if (p2.side == Constants.east) {
System.out.print("p2(" + p2.time + ")");
}
if (p3.side == Constants.east) {
System.out.print("p3(" + p3.time + ")");
}
if (p4.side == Constants.east) {
System.out.print("p4(" + p4.time + ")");
}
System.out.print(" - " + total + " -> ");
if (p1.side == Constants.west) {
System.out.print("p1(" + p1.time + ")");
}
if (p2.side == Constants.west) {
System.out.print("p2(" + p2.time + ")");
}
if (p3.side == Constants.west) {
System.out.print("p3(" + p3.time + ")");
}
if (p4.side == Constants.west) {
System.out.print("p4(" + p4.time + ")");
}
System.out.println();
}
@Test public void testNoHeuristic () {
if (verifyAssertionError()){
run();
}
}
@Test public void testBFSHeuristic() {
if (verifyAssertionError("+search.class=gov.nasa.jpf.search.heuristic.BFSHeuristic")){
run();
}
}
}