git reimport

This commit is contained in:
2019-03-15 13:47:54 +04:00
commit 3b461f73de
489 changed files with 1631603 additions and 0 deletions

BIN
dcj/other/pancakes.class Normal file

Binary file not shown.

25
dcj/other/pancakes.java Normal file
View File

@@ -0,0 +1,25 @@
// Sample input 2, in Java.
public class pancakes {
public pancakes() {
}
public static long GetStackSize() {
return 6L;
}
public static long GetNumDiners() {
return 4L;
}
public static long GetStackItem(long i) {
switch ((int)i) {
case 0: return 0L;
case 1: return 0L;
case 2: return 0L;
case 3: return 2L;
case 4: return 2L;
case 5: return 3L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

23
dcj/other/pancakes0.java Normal file
View File

@@ -0,0 +1,23 @@
// Sample input 1, in Java.
public class pancakes0 {
public pancakes0() {
}
public static long GetStackSize() {
return 4L;
}
public static long GetNumDiners() {
return 4L;
}
public static long GetStackItem(long i) {
switch ((int)i) {
case 0: return 3L;
case 1: return 1L;
case 2: return 2L;
case 3: return 0L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

25
dcj/other/pancakes1.java Normal file
View File

@@ -0,0 +1,25 @@
// Sample input 2, in Java.
public class pancakes1 {
public pancakes1() {
}
public static long GetStackSize() {
return 6L;
}
public static long GetNumDiners() {
return 4L;
}
public static long GetStackItem(long i) {
switch ((int)i) {
case 0: return 0L;
case 1: return 0L;
case 2: return 0L;
case 3: return 2L;
case 4: return 2L;
case 5: return 3L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

26
dcj/other/pancakes2.java Normal file
View File

@@ -0,0 +1,26 @@
// Sample input 3, in Java.
public class pancakes2 {
public pancakes2() {
}
public static long GetStackSize() {
return 7L;
}
public static long GetNumDiners() {
return 5L;
}
public static long GetStackItem(long i) {
switch ((int)i) {
case 0: return 0L;
case 1: return 1L;
case 2: return 3L;
case 3: return 2L;
case 4: return 1L;
case 5: return 3L;
case 6: return 0L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,87 @@
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
new Main().run();
}
int myId = message.MyNodeId();
int nodeCount = message.NumberOfNodes();
long stackSize = pancakes.GetStackSize();
long diners = pancakes.GetNumDiners();
long perNode = stackSize / nodeCount + 1;
long iL = myId * perNode;
long iR = Math.min((myId + 1) * perNode, stackSize);
void run() {
client();
if (myId == 0) {
server();
}
}
long count(List<Long> xs) {
long res = 0;
long prev = -1;
for (Long x : xs) {
if (x < prev) {
res++;
}
prev = x;
}
return res + 1;
}
void client() {
List<Long> xs = new ArrayList<>();
for (long i = iL; i < iR; i++) {
long x = pancakes.GetStackItem(i);
xs.add(x);
}
if (xs.isEmpty()) {
message.PutLL(0, -1);
message.Send(0);
return;
}
long res = count(xs);
message.PutLL(0, xs.get(0));
message.PutLL(0, xs.get(xs.size() - 1));
message.PutLL(0, res);
message.Send(0);
}
void server() {
long res = 0;
long prevLast = Long.MAX_VALUE;
for (int nodeId = 0; nodeId < nodeCount; nodeId++) {
message.Receive(nodeId);
long first = message.GetLL(nodeId);
if (first == -1) {
continue;
}
long last = message.GetLL(nodeId);
long iRes = message.GetLL(nodeId);
// System.out.println("R " + nodeId + ": "+ first + " " + last + " " + iRes);
res += iRes;
if (prevLast <= first) {
res--;
}
prevLast = last;
// System.out.println(res);
}
System.out.println(res);
}
}

32
dcj/src/Main.java Normal file
View File

@@ -0,0 +1,32 @@
public class Main {
public static void main(String[] args) {
new Main().run();
}
int myId = message.MyNodeId();
int nodeCount = message.NumberOfNodes();
long itemCount = 0;
long perNode = itemCount / (nodeCount - 1) + 1;
long iL = Math.min((myId - 1) * perNode, itemCount);
long iR = Math.min(myId * perNode, itemCount);
void run() {
if (myId == 0) {
server();
} else {
client();
}
}
void client() {
}
void server() {
}
}

View File

@@ -0,0 +1,165 @@
import java.util.HashMap;
import java.util.Map;
public class SolutionFlagpoles {
public static void main(String[] args) {
new SolutionFlagpoles().run();
}
int myId = message.MyNodeId();
int nodeCount = message.NumberOfNodes();
long itemCount = flagpoles.GetNumFlagpoles();
long perNode = itemCount / (nodeCount - 1) + 1;
long iL = Math.min((myId - 1) * perNode, itemCount);
long iR = Math.min(myId * perNode, itemCount);
void run() {
if (myId == 0) {
server();
} else {
client();
}
}
static class Sequence {
long d;
long length;
long startIndex;
long startElement;
long endElement;
public Sequence(long d, long length, long startIndex, long startElement, long endElement) {
this.d = d;
this.length = length;
this.startIndex = startIndex;
this.startElement = startElement;
this.endElement = endElement;
}
static Sequence zero(long startIndex) {
return new Sequence(0, 0, startIndex, -1, -1);
}
long endIndex() {
return Math.max(startIndex, startIndex + length - 1);
}
Sequence addElementRight(long element) {
if (endElement == -1) {
return new Sequence(0, 1, startIndex, element, element);
}
if (length == 1) {
return new Sequence(element - startElement, 2, startIndex, startElement, element);
}
if (element - endElement == d) {
return new Sequence(d, length + 1, startIndex, startElement, element);
}
return new Sequence(element - endElement, 2, startIndex + length - 1, endElement, element);
}
Sequence addSequenceRight(Sequence o) {
if (endIndex() + 1 != o.startIndex) {
return o;
}
if (o.length == 1) {
return addElementRight(o.startElement);
}
if (d != o.d) {
if (o.startElement - endElement == o.d) {
return new Sequence(o.d, o.length + 1, o.startIndex - 1, endElement, o.endElement);
}
return o;
}
if (o.startElement - endElement != d) {
return o;
}
return new Sequence(d, length + o.length, startIndex, startElement, o.endElement);
}
boolean isBetter(Sequence o) {
return length > o.length;
}
void send(int target) {
message.PutLL(target, d);
message.PutLL(target, length);
message.PutLL(target, startIndex);
message.PutLL(target, startElement);
message.PutLL(target, endElement);
message.Send(target);
}
static Sequence receive(int source) {
message.Receive(source);
return new Sequence(message.GetLL(source), message.GetLL(source), message.GetLL(source), message.GetLL(source), message.GetLL(source));
}
public String toString() {
return "{" + d + " " + length + " " + startIndex + " " + startElement + " " + endElement + "}";
}
}
void client() {
Map<Long, Long> map = new HashMap<>();
for (long i = iL; i < iR; i++) {
map.put(i, flagpoles.GetHeight(i));
}
Sequence best = Sequence.zero(iL);
Sequence current = Sequence.zero(iL);
Sequence leftmost = Sequence.zero(iL);
for (long i = iL; i < iR; i++) {
current = current.addElementRight(map.get(i));
if (current.startIndex == iL) {
leftmost = current;
}
if (current.isBetter(best)) {
best = current;
}
}
leftmost.send(0);
current.send(0);
best.send(0);
}
void server() {
Sequence best = Sequence.zero(0);
Sequence current = Sequence.zero(0);
for (int clientId = 1; clientId < nodeCount; clientId++) {
Sequence clientLeftmost = Sequence.receive(clientId);
Sequence clientRightmost = Sequence.receive(clientId);
Sequence clientBest = Sequence.receive(clientId);
// System.out.println(clientId + " -> " + clientLeftmost.toString());
// System.out.println(clientId + " -> " + clientBest.toString());
if (clientBest.isBetter(best)) {
best = clientBest;
}
current = current.addSequenceRight(clientLeftmost);
// System.out.println(current.toString());
if (current.isBetter(best)) {
best = current;
}
if (clientRightmost.endIndex() > current.endIndex()) {
current = clientRightmost;
}
}
System.out.println(best.length);
}
}

210
dcj/src/SolutionMemory.java Normal file
View File

@@ -0,0 +1,210 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SolutionMemory {
public static void main(String[] args) {
new SolutionMemory().run();
}
int myId = message.MyNodeId();
int nodeCount = message.NumberOfNodes();
long itemCount = broken_memory.GetLength();
void run() {
client();
if (myId == 0) {
server();
}
}
int binSearchIters = 30;
Map<Long, Long> map;
long hash(long l, long r) {
long res1 = 0;
long res2 = 0;
for (long i = l; i < r; i++) {
res1 *= 31;
res1 += map.get(i);
res1 %= 1000000009L;
res2 *= 7;
res2 += map.get(i);
res2 %= 1000000007L;
}
return res1 + res2;
}
void serveRequest(int source) {
while (true) {
message.Receive(source);
long status = message.GetLL(source);
if (status == 0) {
return;
}
long l = message.GetLL(source);
long r = message.GetLL(source);
long res = hash(l, r);
message.PutLL(source, res);
message.Send(source);
}
}
boolean sendRequestIsMatch(int target, long l, long r) {
message.PutLL(target, 1);
message.PutLL(target, l);
message.PutLL(target, r);
message.Send(target);
long res = hash(l, r);
message.Receive(target);
long otherHash = message.GetLL(target);
// System.out.println(myId + " " + target + " (" + l + " " + r + ") = " + (res == otherHash));
return res == otherHash;
}
Set<Long> decide(int other) {
Set<Long> candidates = new HashSet<>();
{
long leftL = 0;
long leftR = itemCount;
while (leftR - leftL > 1) {
long m1 = (long) (leftL + (leftR - leftL) * 0.3333);
long m2 = (long) (leftL + (leftR - leftL) * 0.6666);
if (m1 - leftL > 0) {
if (!sendRequestIsMatch(other, leftL, m1)) {
leftL = leftL;
leftR = m1;
continue;
}
}
if (m2 - m1 > 0) {
if (!sendRequestIsMatch(other, m1, m2)) {
leftL = m1;
leftR = m2;
continue;
}
}
leftL = m2;
leftR = leftR;
}
candidates.add(leftL);
}
{
long rightL = 0;
long rightR = itemCount;
while (rightR - rightL > 1) {
long m1 = (long) (rightL + (rightR - rightL) * 0.3333);
long m2 = (long) (rightL + (rightR - rightL) * 0.6666);
if (rightR - m2 > 0) {
if (!sendRequestIsMatch(other, m2, rightR)) {
rightL = m2;
rightR = rightR;
continue;
}
}
if (m2 - m1 > 0) {
if (!sendRequestIsMatch(other, m1, m2)) {
rightL = m1;
rightR = m2;
continue;
}
}
rightL = rightL;
rightR = m1;
}
candidates.add(rightL);
}
// System.out.println(leftL + " " + leftR + " | " + rightL + " " + rightR);
message.PutLL(other, 0);
message.Send(other);
// candidates.add(leftL);
// candidates.add(rightL);
return candidates;
}
Set<Long> discuss(int other) {
boolean junior = myId % 2 == 0;
Set<Long> result;
if (junior) {
serveRequest(other);
result = decide(other);
} else {
result = decide(other);
serveRequest(other);
}
return result;
}
void client() {
map = new HashMap<>();
for (int i = 0; i < itemCount; i++) {
map.put((long) i, broken_memory.GetValue(i));
}
int otherClient1;
int otherClient2;
if (myId % 2 == 0) {
otherClient1 = (myId + 1 + nodeCount) % nodeCount;
otherClient2 = (myId - 1 + nodeCount) % nodeCount;
} else {
otherClient1 = (myId - 1 + nodeCount) % nodeCount;
otherClient2 = (myId + 1 + nodeCount) % nodeCount;
}
Set<Long> result1 = discuss(otherClient1);
Set<Long> result2 = discuss(otherClient2);
result1.retainAll(result2);
long x = result1.iterator().next();
// System.out.println(x);
message.PutLL(0, x);
message.Send(0);
}
void server() {
List<Long> list = new ArrayList<>();
for (int i = 0; i < nodeCount; i++) {
message.Receive(i);
list.add(message.GetLL(i));
}
StringBuilder sb = new StringBuilder();
for (long i : list) {
sb.append(i);
sb.append(' ');
}
System.out.println(sb);
}
}

32
dcj/src/Template.java Normal file
View File

@@ -0,0 +1,32 @@
public class Template {
public static void main(String[] args) {
new Template().run();
}
int myId = message.MyNodeId();
int nodeCount = message.NumberOfNodes();
long itemCount = 0;
long perNode = itemCount / (nodeCount - 1) + 1;
long iL = Math.min((myId - 1) * perNode, itemCount);
long iR = Math.min(myId * perNode, itemCount);
void run() {
if (myId == 0) {
server();
} else {
client();
}
}
void client() {
}
void server() {
}
}

View File

@@ -0,0 +1,17 @@
// Sample input 2, in Java.
public class broken_memory {
public broken_memory() {
}
public static long GetLength() {
return 30L;
}
public static long GetValue(long i) {
if (i < 0L || i >= GetLength())
throw new IllegalArgumentException("Invalid argument");
if ((29L - message.MyNodeId()) == i)
return (((i % 9L) + 1L) * ((i % 7L) + 1L) ^ (i + message.MyNodeId() + 1)) + 1L;
return (((i % 9L) + 1L) * ((i % 7L) + 1L)) + 1L;
}
}

92
dcj/src/message.java Normal file
View File

@@ -0,0 +1,92 @@
// A program you submit to Distributed Code Jam will be compiled by Google, and
// will run on multiple computers (nodes). This library describes the interface
// needed for the nodes to identify themselves and to communicate.
//
// This is the version of the interface for programs written in Java. Your
// program doesn't need to import it and should always use a class name before
// accessing the static methods, e.g.:
// int n = message.NumberOfNodes();
public class message {
// The number of nodes on which the solution is running.
public static int NumberOfNodes() {
return 0;
}
// The index (in the range [0 .. NumberOfNodes()-1]) of the node on which this
// process is running.
public static int MyNodeId() {
return 0;
}
// In all the functions below, if "target" or "source" is not in the valid
// range, the behaviour is undefined.
// The library internally has a message buffer for each of the nodes in
// [0 .. NumberOfNodes()-1]. It accumulates the message in such a buffer through
// the "Put" methods.
// Append "value" to the message that is being prepared for the node with id
// "target". The "Int" in PutInt is interpreted as 32 bits, regardless of
// whether the actual int type will be 32 or 64 bits.
public static void PutChar(int target, char value) {
}
public static void PutInt(int target, int value) {
}
public static void PutLL(int target, long value) {
}
// Send the message that was accumulated in the appropriate buffer to the
// "target" instance, and clear the buffer for this instance.
//
// This method is non-blocking - that is, it does not wait for the receiver to
// call "Receive", it returns immediately after sending the message.
public static void Send(int target) {
}
// The library also has a receiving buffer for each instance. When you call
// "Receive" and retrieve a message from an instance, the buffer tied to this
// instance is overwritten. You can then retrieve individual parts of the
// message through the Get* methods. You must retrieve the contents of the
// message in the order in which they were appended.
//
// This method is blocking - if there is no message to receive, it will wait for
// the message to arrive.
//
// You can call Receive(-1) to retrieve a message from any source, or with
// source in [0 .. NumberOfNodes()-1] to retrieve a message from a particular
// source.
//
// It returns the number of the instance which sent the message (which is equal
// to source, unless source is -1).
public static int Receive(int source) {
return 0;
}
// Each of these methods returns and consumes one item from the buffer of the
// appropriate instance. You must call these methods in the order in which the
// elements were appended to the message (so, for instance, if the message was
// created with PutChar, PutChar, PutLL, you must call GetChar, GetChar, GetLL
// in this order).
// If you call them in different order, or you call a Get* method after
// consuming all the contents of the buffer, behaviour is undefined.
// The "Int" in GetInt is interpreted as 32 bits, regardless of whether the
// actual int type will be 32 or 64 bits.
public static char GetChar(int source) {
return 0;
}
public static int GetInt(int source) {
return 0;
}
public static long GetLL(int source) {
return 0;
}
}

36
dcj/src/number_bases.java Normal file
View File

@@ -0,0 +1,36 @@
// Sample input 1, in Java.
public class number_bases {
public number_bases() {
}
public static long GetLength() {
return 3L;
}
public static long GetDigitX(long i) {
switch ((int)i) {
case 0: return 3L;
case 1: return 2L;
case 2: return 1L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetDigitY(long i) {
switch ((int)i) {
case 0: return 6L;
case 1: return 5L;
case 2: return 4L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetDigitZ(long i) {
switch ((int)i) {
case 0: return 0L;
case 1: return 8L;
case 2: return 5L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,17 @@
// Sample input 1, in Java.
public class broken_memory {
public broken_memory() {
}
public static long GetLength() {
return 10L;
}
public static long GetValue(long i) {
if (i < 0L || i >= GetLength())
throw new IllegalArgumentException("Invalid argument");
if ((message.MyNodeId()) == i)
return (i ^ (i + message.MyNodeId() + 1)) + 1L;
return (i) + 1L;
}
}

View File

@@ -0,0 +1,17 @@
// Sample input 2, in Java.
public class broken_memory {
public broken_memory() {
}
public static long GetLength() {
return 30L;
}
public static long GetValue(long i) {
if (i < 0L || i >= GetLength())
throw new IllegalArgumentException("Invalid argument");
if ((29L - message.MyNodeId()) == i)
return (((i % 9L) + 1L) * ((i % 7L) + 1L) ^ (i + message.MyNodeId() + 1)) + 1L;
return (((i % 9L) + 1L) * ((i % 7L) + 1L)) + 1L;
}
}

View File

@@ -0,0 +1,17 @@
// Sample input 3, in Java.
public class broken_memory {
public broken_memory() {
}
public static long GetLength() {
return 16L;
}
public static long GetValue(long i) {
if (i < 0L || i >= GetLength())
throw new IllegalArgumentException("Invalid argument");
if ((12L ^ message.MyNodeId()) == i)
return ((i * i) ^ 21L ^ (i + message.MyNodeId() + 1)) + 1L;
return ((i * i) ^ 21L) + 1L;
}
}

22
dcj/tasks/flagpoles0.java Normal file
View File

@@ -0,0 +1,22 @@
// Sample input 1, in Java.
public class flagpoles {
public flagpoles() {
}
public static long GetNumFlagpoles() {
return 7L;
}
public static long GetHeight(long i) {
switch ((int)i) {
case 0: return 5L;
case 1: return 7L;
case 2: return 5L;
case 3: return 3L;
case 4: return 1L;
case 5: return 2L;
case 6: return 3L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

19
dcj/tasks/flagpoles1.java Normal file
View File

@@ -0,0 +1,19 @@
// Sample input 2, in Java.
public class flagpoles {
public flagpoles() {
}
public static long GetNumFlagpoles() {
return 4L;
}
public static long GetHeight(long i) {
switch ((int)i) {
case 0: return 2L;
case 1: return 2L;
case 2: return 2L;
case 3: return 2L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

20
dcj/tasks/flagpoles2.java Normal file
View File

@@ -0,0 +1,20 @@
// Sample input 3, in Java.
public class flagpoles {
public flagpoles() {
}
public static long GetNumFlagpoles() {
return 5L;
}
public static long GetHeight(long i) {
switch ((int)i) {
case 0: return 1L;
case 1: return 3L;
case 2: return 2L;
case 3: return 4L;
case 4: return 3L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,36 @@
// Sample input 1, in Java.
public class number_bases {
public number_bases() {
}
public static long GetLength() {
return 3L;
}
public static long GetDigitX(long i) {
switch ((int)i) {
case 0: return 3L;
case 1: return 2L;
case 2: return 1L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetDigitY(long i) {
switch ((int)i) {
case 0: return 6L;
case 1: return 5L;
case 2: return 4L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetDigitZ(long i) {
switch ((int)i) {
case 0: return 0L;
case 1: return 8L;
case 2: return 5L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,36 @@
// Sample input 2, in Java.
public class number_bases {
public number_bases() {
}
public static long GetLength() {
return 3L;
}
public static long GetDigitX(long i) {
switch ((int)i) {
case 0: return 1000L;
case 1: return 20000L;
case 2: return 300000L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetDigitY(long i) {
switch ((int)i) {
case 0: return 40000L;
case 1: return 500000L;
case 2: return 6L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetDigitZ(long i) {
switch ((int)i) {
case 0: return 41000L;
case 1: return 520000L;
case 2: return 300006L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,39 @@
// Sample input 3, in Java.
public class number_bases {
public number_bases() {
}
public static long GetLength() {
return 4L;
}
public static long GetDigitX(long i) {
switch ((int)i) {
case 0: return 5L;
case 1: return 3L;
case 2: return 0L;
case 3: return 0L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetDigitY(long i) {
switch ((int)i) {
case 0: return 5L;
case 1: return 3L;
case 2: return 0L;
case 3: return 0L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetDigitZ(long i) {
switch ((int)i) {
case 0: return 0L;
case 1: return 6L;
case 2: return 0L;
case 3: return 0L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,21 @@
// Sample input 1, in Java.
public class query_of_death {
public query_of_death() {
}
static int isthenodebroken = 0;
static int testvs[] = {1, 1, 0};
public static long GetLength() {
return 3L;
}
public static long GetValue(long i) {
if (i < 0L || i >= GetLength())
throw new IllegalArgumentException("Invalid argument");
int val = (int)(testvs[(int)i]^((int)(Math.random() * 2 + 1) % (isthenodebroken + 1)));
if (i == 1)
isthenodebroken = 1;
return val;
}
}

View File

@@ -0,0 +1,21 @@
// Sample input 2, in Java.
public class query_of_death {
public query_of_death() {
}
static int isthenodebroken = 0;
static int testvs[] = {1, 1, 1};
public static long GetLength() {
return 3L;
}
public static long GetValue(long i) {
if (i < 0L || i >= GetLength())
throw new IllegalArgumentException("Invalid argument");
int val = (int)(testvs[(int)i]^((int)(Math.random() * 2 + 1) % (isthenodebroken + 1)));
if (i == 0)
isthenodebroken = 1;
return val;
}
}

View File

@@ -0,0 +1,21 @@
// Sample input 3, in Java.
public class query_of_death {
public query_of_death() {
}
static int isthenodebroken = 0;
static int testvs[] = {1, 0, 1, 0, 1};
public static long GetLength() {
return 5L;
}
public static long GetValue(long i) {
if (i < 0L || i >= GetLength())
throw new IllegalArgumentException("Invalid argument");
int val = (int)(testvs[(int)i]^((int)(Math.random() * 2 + 1) % (isthenodebroken + 1)));
if (i == 4)
isthenodebroken = 1;
return val;
}
}

View File

@@ -0,0 +1,131 @@
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class Main {
public static void main(String[] args) {
new Main().run();
}
int myId = message.MyNodeId();
int nodeCount = message.NumberOfNodes();
long aLength = todd_and_steven.GetToddLength();
long bLength = todd_and_steven.GetStevenLength();
long itemCount = (long) 5.05e9;
long perNode = itemCount / nodeCount + 1;
long iL = Math.min(myId * perNode, itemCount);
long iR = Math.min((myId + 1) * perNode, itemCount);
void run() {
client();
if (myId == 0) {
server();
}
}
long mul(long a, long b) {
return (a * b) % MOD;
}
long add(long a, long b) {
return (a + b) % MOD;
}
long MOD = 1000000007L;
long BIG = 1000000000000L;
long get(int type, long i) {
if (type == 0) {
if (i >= aLength) {
return BIG;
}
return todd_and_steven.GetToddValue(i);
}
if (type == 1) {
if (i >= bLength) {
return BIG;
}
return todd_and_steven.GetStevenValue(i);
}
throw new IllegalArgumentException();
}
long binSearch(int type, long minVal) {
long l = 0;
long r = BIG;
while (r - l > 1) {
long m = (l + r) / 2;
long x = get(type, m);
if (x < minVal) {
l = m;
} else {
r = m;
}
}
if (get(type, l) < minVal) {
l++;
}
return l;
}
void client() {
long minVal = iL;
long maxVal = iR;
long ai = binSearch(0, minVal);
long bi = binSearch(1, minVal);
long nextA = get(0, ai);
long nextB = get(1, bi);
long pos = ai + bi;
long res = 0;
while (true) {
long x = Math.min(nextA, nextB);
if (x >= maxVal) {
break;
}
res = add(res, x ^ pos);
pos++;
if (x == nextA) {
ai++;
nextA = get(0, ai);
}
if (x == nextB) {
bi++;
nextB = get(1, bi);
}
}
message.PutLL(0, res);
message.Send(0);
}
void server() {
long res = 0;
for (int i = 0; i < nodeCount; i++) {
message.Receive(i);
long x = message.GetLL(i);
res = add(res, x);
}
System.out.println(res);
}
}

View File

@@ -0,0 +1,28 @@
// Sample input 1, in Java.
public class todd_and_steven {
public todd_and_steven() {
}
public static long GetToddLength() {
return 1L;
}
public static long GetStevenLength() {
return 2L;
}
public static long GetToddValue(long i) {
switch ((int)i) {
case 0: return 3L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetStevenValue(long i) {
switch ((int)i) {
case 0: return 2L;
case 1: return 6L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,27 @@
// Sample input 2, in Java.
public class todd_and_steven {
public todd_and_steven() {
}
public static long GetToddLength() {
return 1L;
}
public static long GetStevenLength() {
return 1L;
}
public static long GetToddValue(long i) {
switch ((int)i) {
case 0: return 101L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetStevenValue(long i) {
switch ((int)i) {
case 0: return 100L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,31 @@
// Sample input 3, in Java.
public class todd_and_steven {
public todd_and_steven() {
}
public static long GetToddLength() {
return 2L;
}
public static long GetStevenLength() {
return 4L;
}
public static long GetToddValue(long i) {
switch ((int)i) {
case 0: return 15L;
case 1: return 23L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
public static long GetStevenValue(long i) {
switch ((int)i) {
case 0: return 4L;
case 1: return 8L;
case 2: return 16L;
case 3: return 42L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,160 @@
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class Main {
public static void main(String[] args) {
new Main().run();
}
int myId = message.MyNodeId();
int nodeCount = message.NumberOfNodes();
long numberLength = weird_editor.GetNumberLength();
long perNode = numberLength / nodeCount + 1;
long iL = Math.min(myId * perNode, numberLength);
long iR = Math.min((myId + 1) * perNode, numberLength);
void run() {
client();
if (myId == 0) {
server();
}
}
long mul(long a, long b) {
return (a * b) % MOD;
}
long add(long a, long b) {
return (a + b) % MOD;
}
long merge(long res1, long l1, long res2, long l2) {
return add(mul(res1, pow(10, l2)), res2);
}
long calcSeries(long digit, int n) {
long res = 0;
long d = 1;
for (int i = 0; i < n; i++) {
res = add(res, mul(d, digit));
d = mul(d, 10);
}
return res;
}
long MOD = 1000000007L;
long pow(long a, long n) {
if (n == 0) {
return 1 % MOD;
}
if (n == 1) {
return a % MOD;
}
if (n % 2 == 0) {
long x = pow(a, n / 2);
return mul(x, x);
}
return mul(pow(a, n - 1), a);
}
void client() {
// System.out.println("[" + iL + ", " + iR + "]");
List<Long> digits = new ArrayList<>();
for (long i = iL; i < iR; i++) {
long x = weird_editor.GetDigit(i);
digits.add(x);
}
List<Long> res = new ArrayList<>();
long[] counts = new long[10];
int rightmost = -1;
for (int digit = 9; digit >= 1; digit--) {
for (int i = rightmost + 1; i < digits.size(); i++) {
if (digits.get(i) == digit) {
counts[digit]++;
rightmost = i;
}
}
}
long[] ress = new long[10];
long[] ressLen = new long[10];
for (int i = 0; i < 10; i++) {
ress[i] = calcSeries(i, (int) counts[i]);
ressLen[i] = counts[i];
}
for (int i = 8; i >= 0; i--) {
ress[i] = merge(ress[i + 1], ressLen[i + 1], ress[i], ressLen[i]);
ressLen[i] += ressLen[i + 1];
}
putLLArray(0, counts);
putLLArray(0, ress);
putLLArray(0, ressLen);
message.Send(0);
// System.out.println("counts " + Arrays.toString(counts));
// System.out.println("ress " + Arrays.toString(ress));
// System.out.println("ressLen " + Arrays.toString(ressLen));
}
void putLLArray(int target, long[] a) {
message.PutInt(target, a.length);
for (int i = 0; i < a.length; i++) {
message.PutLL(target, a[i]);
}
}
long[] getLLArray(int source) {
int len = message.GetInt(source);
long[] a = new long[len];
for (int i = 0; i < len; i++) {
a[i] = message.GetLL(source);
}
return a;
}
void server() {
long totalRes = 0;
long totalLen = 0;
int maxDigit = 0;
for (int i = nodeCount - 1; i >= 0; i--) {
message.Receive(i);
long[] counts = getLLArray(i);
long[] ress = getLLArray(i);
long[] ressLen = getLLArray(i);
int newMaxDigit = maxDigit;
for (int j = maxDigit + 1; j < 10; j++) {
if (counts[j] != 0) {
newMaxDigit = j;
}
}
// totalRes = merge(totalRes, totalLen, ress[maxDigit], ressLen[maxDigit]);
totalRes = merge(ress[maxDigit], ressLen[maxDigit], totalRes, totalLen);
totalLen += ressLen[maxDigit];
maxDigit = newMaxDigit;
}
long zeroCount = numberLength - totalLen;
totalRes = mul(totalRes, pow(10, zeroCount));
System.out.println(totalRes);
}
}

View File

@@ -0,0 +1,19 @@
// Sample input 1, in Java.
public class weird_editor {
public weird_editor() {
}
public static long GetNumberLength() {
return 4L;
}
public static long GetDigit(long i) {
switch ((int)i) {
case 0: return 3L;
case 1: return 0L;
case 2: return 0L;
case 3: return 1L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,23 @@
// Sample input 2, in Java.
public class weird_editor {
public weird_editor() {
}
public static long GetNumberLength() {
return 8L;
}
public static long GetDigit(long i) {
switch ((int)i) {
case 0: return 1L;
case 1: return 0L;
case 2: return 2L;
case 3: return 3L;
case 4: return 2L;
case 5: return 1L;
case 6: return 3L;
case 7: return 0L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

View File

@@ -0,0 +1,25 @@
// Sample input 3, in Java.
public class weird_editor {
public weird_editor() {
}
public static long GetNumberLength() {
return 10L;
}
public static long GetDigit(long i) {
switch ((int)i) {
case 0: return 4L;
case 1: return 4L;
case 2: return 3L;
case 3: return 3L;
case 4: return 2L;
case 5: return 1L;
case 6: return 0L;
case 7: return 0L;
case 8: return 0L;
case 9: return 9L;
default: throw new IllegalArgumentException("Invalid argument");
}
}
}

110
dcj/tool/README Normal file
View File

@@ -0,0 +1,110 @@
INSTALLATION
Unpack the archive in a place convienient for you. If you downloaded 'minimal'
version build parunner (see [RE]BUILDING parunner) and create config.json file
(in the directory to which you unpacked the archive) (see CONFIGURATION).
USAGE
See python dcj.py -h
[RE]BUILDING parunner
* Make sure go is installed (installation instructions are on
https://golang.org/doc/install).
* Build parunner by going to src/parunner/ subdirectory and running
go build
* Copy executable (parunner file) to executable/ subdirectory:
cp parunner ../../executable/
CONFIGURATION
Configuration is stored in config.json file, located in the directory to which
you unpacked the archive. Some sample configs are provided in sample-configs/
subdirectory.
The configuration file should contain a single object, with the following
fields:
* c-compiler - command that will be used to compile files written in C.
* c-compiler-flags - list of flags with which files written in C will be
compiled.
* cpp-compiler - command that will be used to compile files written in C++.
* cpp-compiler-flags - list of flags with which files written in C++ will be
compiled.
* java-compiler - command used to compile .java files.
* java-compiler-classpath-arg - argument of java-compiler which specifies Java
class path to be used by the compiler.
* java-include-dirs - list of directories containing includes necessary for
compilation .c files with implementation of Java libraries.
* java-native-library-linker - command used to link implementation of Java
library.
* java-native-library-linker-options - list of options passed to
java-native-library-linker.
* java-native-library-name - name of file containing native implementation of
message class for Java.
* java-wrapper-file-content - script that will be used to run your solution.
Use {0} as a placeholder for directory containing .class file of your
solution and {1} as a placeholder for directory containing libraries used by
your solution.
* parunner-file - name of parunner executable.
You may figure proper values by building and running a simple program using
message library manually (see BUILDING AND RUNNING SOLUTIONS MANUALLY).
BUILDING AND RUNNING SOLUTIONS MANUALLY
* If you are using Java or Python:
* Install SWIG (http://swig.org).
* Generate wrappers for language of your choice.
* For Java:
swig -java src/swig/message.i
* For Python:
swig -python src/swig/message.i
* Build message library from
libraries/message_internal.c
libraries/zeus_local.c
and files generated by SWIG (see http://www.swig.org/tutorial.html for
reference).
* Build solution.
* Run the solution using pa runner:
parunner path-to-built-soluton -n=number-of-simulated-hosts
INSTALLING MinGW TO WORK WITH THE TOOL
* Install MinGW, following instructions on:
http://www.mingw.org/wiki/Getting_Started
make sure the following packages will be installed:
* mingw32-binutils
* mingw32-gcc
* mingw32-gcc-g++
* mingw32-libz
* msys-base
* msys-console
* msys-zlib
* Install python from:
https://www.python.org/downloads/release/python-2710/
* Add the following line (replacing /c/python27 if you didn't use default
installation directory for Python):
export PATH="$PATH:/c/python27"
to file (replacing your-user-name)(replace C:\MinGW if you did not use
default MinGW instalation directory):
c:\MinGW\msys\1.0\home\your-user-name\.bashrc
* Open terminal by running:
C:\MinGW\msys\1.0\msys.bat
* Use the tool:
python path/to/dcj.py

27
dcj/tool/config.json Normal file
View File

@@ -0,0 +1,27 @@
{
"c-compiler": "gcc",
"c-compiler-flags": [
"-O2",
"-lm"
],
"cpp-compiler": "g++",
"cpp-compiler-flags": [
"-O2",
"-std=gnu++0x",
"-lm"
],
"java-compiler": "javac",
"java-compiler-classpath-arg": "-classpath",
"java-include-dirs": [
"/System//Library/Frameworks/JavaVM.framework/Versions/A/Headers"
],
"java-native-library-linker": "cc",
"java-native-library-linker-options": [
"-framework",
"JavaVM",
"-bundle"
],
"java-native-library-name": "libmessage.jnilib",
"java-wrapper-file-content": "#!/bin/bash\ncd {0}\n/usr/bin/java -Djava.library.path={1} -classpath {1} Wrapper\n",
"parunner-file": "parunner"
}

73
dcj/tool/dcj.py Normal file
View File

@@ -0,0 +1,73 @@
"""CLI for local testing of solutions in Distributed Code Jam."""
import argparse
import os
from os import chmod
import stat
import subprocess
import sys
from dcj import build
from dcj import command_chooser
from dcj import configuration
from dcj import run
from dcj import test
def _print(x):
print ' '.join(x)
return 0 # Tell tool that command execution was succesfull.
def _subprocess_call_with_error_catching(command):
try:
subprocess.call(command)
return 0
except OSError as e:
if e.args == (2, 'No such file or directory'):
raise ValueError('Command {0} not found.'.format(command[0]))
else:
raise ValueError(
'Error when executing command {0!r}: {1!r}.'.format(command, e))
def _create_script(script_path, content):
with open(script_path, 'w') as f:
f.write(content)
chmod(script_path, stat.S_IRWXU | stat.S_IROTH | stat.S_IXOTH)
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='dcj')
config = configuration.Configuration()
# TODO(jbartosik): allow using different configs.
config.Load(
os.path.join(os.path.dirname(os.path.realpath(__file__)), 'config.json'))
builder = build.Build(config)
runner = run.Run(config)
tester = test.Tester(builder, runner)
chooser = command_chooser.CommandChooser({
'build': builder,
'run': runner,
'test': tester,
})
chooser.AddToParser(parser)
parser.add_argument(
'--dry-run',
action='store_true',
help='Only print commands, don\'t execute them.',
default=False)
args = parser.parse_args()
if args.dry_run:
builder.SetCommandExecutor(_print)
runner.SetCommandExecutor(_print)
else:
builder.SetCommandExecutor(_subprocess_call_with_error_catching)
builder.SetScriptCreator(_create_script)
runner.SetCommandExecutor(_subprocess_call_with_error_catching)
try:
chooser.Run(args)
except (NotImplementedError, RuntimeError, ValueError) as error:
print error
sys.exit(1)

6
dcj/tool/dcj.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
DIRECTORY=$( cd "$( dirname $0 )" && pwd )
PYTHON="/usr/bin/python2.7"
$PYTHON $DIRECTORY/dcj.py $@

1
dcj/tool/dcj/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""CLI for DCJ."""

BIN
dcj/tool/dcj/__init__.pyc Normal file

Binary file not shown.

344
dcj/tool/dcj/build.py Normal file
View File

@@ -0,0 +1,344 @@
"""Class for building DCJ test executables."""
from os import path
class Build(object):
"""A class for building DCJ test executables."""
def __init__(self, config):
self._command_executor = lambda x: None
self._script_creator = lambda x, y: None
self._config = config
def SetCommandExecutor(self, command_executor):
self._command_executor = command_executor
def SetScriptCreator(self, script_creator):
self._script_creator = script_creator
def AddToParser(self, parser):
"""Adds flags to parser and returns it."""
parser.add_argument('--source', required=True,
help='source file of the solution.')
parser.add_argument('--language',
help='language of the solution. Valid choices are: '
'{0}. If the flag is not provided language will be '
'deduced from source file extensions.'
.format(
', '.join(self._SUPPORTED_LANGUAGE_TO_EXTENSION))
)
parser.add_argument('--library',
help='source file of library generating input.')
parser.add_argument('--executable',
help='path of the executable to be built. By default '
'it\'s the same as path of source with removed '
'filename extension.')
parser.add_argument('--extra_flags',
help='comma-separated list of additional flags to pass '
'to compiler. For '
'example --extra_flags="-Wall,-Wextra".')
return parser
def Run(self, args):
# TODO(jabrtosik): When running python tell user / check if file meets
# necessary conditions:
# * Is executable.
# * First line is #!/path/to/interpreter.
self._ValidateArgs(args)
source_extension = self._SourceExtension(args)
commands_builder = self._BUILDER_FOR_EXTENSION[source_extension]
for command in commands_builder(self, args):
if self._command_executor(command) != 0:
raise RuntimeError('Build failed.')
def Description(self):
return 'Builds solution for local testing.'
def _ValidateArgs(self, args):
"""Validate arguments.
Args:
args: arguments to be validated.
Raises:
ValueError: exception with string describing the problem detected.
"""
if 'language' in args and args['language']:
if args['language'] not in self._SUPPORTED_LANGUAGE_TO_EXTENSION:
raise ValueError('--language must be one of {0!r} but it was {1!r}.'
.format(self._SUPPORTED_LANGUAGE_TO_EXTENSION.keys(),
args['language']))
else:
# Skip file extension validations.
return
source_extension = self._SourceExtension(args)
if source_extension not in self._BUILDER_FOR_EXTENSION:
raise ValueError('Source extension must be one of {0!r} but it was {1!r}.'
.format(self._BUILDER_FOR_EXTENSION.keys(),
source_extension))
if self._HasLibrary(args):
library_extension = self._LibraryExtension(args)
if source_extension == '.c':
if library_extension != '.c' and library_extension != '.h':
raise ValueError('C solutions should have a .h or .c library')
elif source_extension == '.cc' or source_extension == '.cpp':
if (library_extension != '.cc' and library_extension != '.cpp' and
library_extension != '.c' and library_extension != '.h'):
raise ValueError('C++ solutions should have a .cc/.cpp or .h library')
elif source_extension == '.py':
if library_extension != '.py':
raise ValueError('Python solutions should have a .py library')
elif source_extension == '.java':
if library_extension != '.java':
raise ValueError('Java solutions should have a .java library')
def _CBuildCommands(self, args):
"""Prepare commands to build solution written in C.
Args:
args: arguments of the build.
Returns:
tuple in which each item is a tuple with command that will execute a step
of building solution.
"""
compiler = self._config.GetStringConfigValue('c-compiler')
dcj_root = path.join(path.dirname(path.realpath(__file__)), '..')
include_dir = path.join(dcj_root, 'includes')
local_zeus_path = path.join(dcj_root, 'libraries', 'zeus_local.c')
message_path = path.join(dcj_root, 'libraries', 'message_internal.c')
# TODO(jbartosik): support compilers that don't have -I flag for include
# dirs.
compiler_args = (
self._config.GetStringListConfigValue('c-compiler-flags') + [
'-I' + include_dir,
local_zeus_path, message_path,
]
)
if self._HasLibrary(args):
compiler_args += [args['library']]
build_solution_command = (
(compiler,) + tuple(compiler_args) + self.ExtraFlags(args) +
(args['source'], '-o', self.ExecutablePath(args),)
)
return (build_solution_command,)
def _CcBuildCommands(self, args):
"""Prepare commands to build solution written in C++.
Args:
args: arguments of the build.
Returns:
tuple in which each item is a tuple with command that will execute a step
of building solution.
"""
# TODO(jbartosik): support other compilers.
dcj_root = path.join(path.dirname(path.realpath(__file__)), '..')
include_dir = path.join(dcj_root, 'includes')
c_compiler_with_flags = tuple(
[self._config.GetStringConfigValue('c-compiler')] +
self._config.GetStringListConfigValue('c-compiler-flags') +
['-I' + include_dir]
)
cpp_compiler_with_flags = tuple(
[self._config.GetStringConfigValue('cpp-compiler')] +
self._config.GetStringListConfigValue('cpp-compiler-flags') +
['-I' + include_dir]
)
c_sources = (path.join(dcj_root, 'libraries', 'zeus_local.c'),
path.join(dcj_root, 'libraries', 'message_internal.c'),
)
c_object_files_build_commands = tuple(
c_compiler_with_flags +
(source_file, '-c', '-o', path.splitext(source_file)[0] + '.o')
for source_file in c_sources
)
object_files = tuple(
path.splitext(source_file)[0] + '.o' for source_file in c_sources
)
files = [args['source']]
if self._HasLibrary(args):
files += [args['library']]
build_solution_command = (
cpp_compiler_with_flags + self.ExtraFlags(args) + object_files +
tuple(files) + ('-o', self.ExecutablePath(args),)
)
return c_object_files_build_commands + (build_solution_command,)
def _BuildPythonObjectFileCommand(self, c_source, output):
dcj_root = path.join(path.dirname(path.realpath(__file__)), '..')
return (
'gcc', '-c', '-fpic',
# TODO(jbartosik): Don't rely on users having this exact version of
# python.
'-I/usr/include/python2.7',
'-I' + path.join(dcj_root, 'includes'),
path.join(dcj_root, 'libraries', c_source),
'-o', path.join(dcj_root, 'libraries', output),
)
def _BuildJavaObjectFileCommand(self, c_source, output):
"""Return command building .c file to work with Java via SWIG."""
compiler = self._config.GetStringConfigValue('c-compiler')
dcj_root = path.join(path.dirname(path.realpath(__file__)), '..')
include_dir = path.join(dcj_root, 'includes')
# TODO(jbartosik): support compilers that don't have -I flag for include
# dirs.
compiler_args = (
'-c', '-fpic',
'-I' + include_dir,
)
java_include_options = tuple(
[
'-I' + directory
for directory in
self._config.GetDirListConfigValue('java-include-dirs')
]
)
dcj_root = path.join(path.dirname(path.realpath(__file__)), '..')
return (compiler,) + compiler_args + java_include_options + (
path.join(dcj_root, 'libraries', c_source),
'-o', path.join(dcj_root, 'libraries', output),
)
def _PyBuildCommands(self, unused_args):
"""Returns tuple with commands for building Python solutions."""
dcj_root = path.join(path.dirname(path.realpath(__file__)), '..')
# TODO(jbartosik): use another directory to store object files.
build_object_file_commands = tuple(
self._BuildPythonObjectFileCommand(item + '.c', item + '.o')
for item in self._MESSAGE_SO_PYTHON_INGREDIENTS)
object_files = tuple(
path.join(dcj_root, 'libraries', item + '.o')
for item in self._MESSAGE_SO_PYTHON_INGREDIENTS
)
link_object_files = (
('ld', '-shared',) +
object_files +
('-o', path.join(dcj_root, 'libraries', '_message.so',))
)
return build_object_file_commands + (link_object_files,)
def _JavaBuildCommands(self, args):
"""Prepare commands to build solution written in Java.
Args:
args: arguments of the build.
Returns:
tuple in which each item is a tuple with command that will execute a step
of building solution.
"""
# Prepare a script that will run java solution. This step is needed because
# parunner works only with single executable files.
solution_class_dir = path.dirname(path.realpath(args['source']))
dcj_root = path.join(path.dirname(path.realpath(__file__)), '..')
message_dir = path.join(dcj_root, 'libraries')
library_dir = path.dirname(path.realpath(args['library']))
classpath = ':'.join((message_dir, library_dir,))
self._script_creator(
self.ExecutablePath(args),
self._config.GetStringConfigValue('java-wrapper-file-content').format(
solution_class_dir,
classpath,
)
)
# Build message_.o and libmessage.so
# TODO(jbartosik): deduplicate with Python
build_object_file_commands = tuple(
self._BuildJavaObjectFileCommand(item + '.c', item + '.o')
for item in self._MESSAGE_SO_JAVA_INGREDIENTS)
object_files = [
path.join(dcj_root, 'libraries', item + '.o')
for item in self._MESSAGE_SO_JAVA_INGREDIENTS]
link_object_files = tuple(
[self._config.GetStringConfigValue('java-native-library-linker')] +
self._config.GetStringListConfigValue(
'java-native-library-linker-options') +
object_files +
['-o', path.join(
dcj_root, 'libraries',
self._config.GetStringConfigValue('java-native-library-name'),
)
]
)
# Create a class file to be ran.
build_class_file_command = (
self._config.GetStringConfigValue('java-compiler'),
path.join(dcj_root, 'libraries', 'Wrapper.java'),
args['source'],
path.join(dcj_root, 'libraries', 'message.java'),
path.join(dcj_root, 'libraries', 'messageJNI.java'),
self._config.GetStringConfigValue('java-compiler-classpath-arg'),
classpath,
)
return (
build_object_file_commands +
(link_object_files, build_class_file_command,)
)
def _SourceExtension(self, args):
if 'language' in args and args['language']:
return self._SUPPORTED_LANGUAGE_TO_EXTENSION[args['language']]
return path.splitext(args['source'])[1]
def _LibraryExtension(self, args):
return path.splitext(args['library'])[1]
def ExtraFlags(self, args):
if 'extra_flags' in args and args['extra_flags']:
return tuple(args['extra_flags'].split(','))
return ()
def ExecutablePath(self, args):
if args['executable']:
return args['executable']
if self._SourceExtension(args) == '.py':
return args['source']
return path.splitext(args['source'])[0]
def _HasLibrary(self, args):
return 'library' in args and args['library'] is not None
_BUILDER_FOR_EXTENSION = {
'.c': _CBuildCommands,
'.cc': _CcBuildCommands,
'.cpp': _CcBuildCommands,
'.java': _JavaBuildCommands,
'.py': _PyBuildCommands,
}
_MESSAGE_SO_PYTHON_INGREDIENTS = (
'message_internal',
'message_wrap_python',
'zeus_local',
)
_MESSAGE_SO_JAVA_INGREDIENTS = (
'message_internal',
'message_wrap_java',
'zeus_local',
)
_SUPPORTED_LANGUAGE_TO_EXTENSION = {
'C': '.c',
'C++': '.cc',
'Java': '.java',
'Python': '.py',
}

BIN
dcj/tool/dcj/build.pyc Normal file

Binary file not shown.

View File

@@ -0,0 +1,41 @@
"""Class for choosing cli commands."""
import argparse
class CommandChooser(object):
"""Chooses command to run based on commandline arguments."""
def __init__(self, commands_dict):
"""Initialize CommandChooser.
Args:
commands_dict: dict from command name to object responsible for executing
the command. The object should provide two methods:
* AddToParser(parser) returning parser to parse arguments passed to the
command.
* Decription() returning string that will describe the command when
using --help flag.
* Run(args) runing the commands with given args.
"""
self.commands_dict = commands_dict
def AddToParser(self, parser):
"""Returns parser that should be used to parse arguments for Run().
Args:
parser: parse to which commands will be added.
"""
subparsers = parser.add_subparsers(title='Command to perform')
for (command, executor) in sorted(self.commands_dict.iteritems()):
parser_command = subparsers.add_parser(
command,
help=executor.Description(),
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
conflict_handler='resolve',
)
executor.AddToParser(parser_command)
parser_command.set_defaults(func=executor.Run)
def Run(self, args):
args.func(vars(args))

Binary file not shown.

View File

@@ -0,0 +1,89 @@
"""Class for getting configuration and nicely reporting problems (if any)."""
import json
from os import path
class Configuration(object):
"""Class for getting configuration and nicely reporting problems (if any)."""
def __init__(self):
self._parsed_config = {}
self._config_path = ''
def Load(self, config_path):
"""Load configuration from specified file.
The file should be readable, it should be a properly formated JSON and it
should contain a single Object.
Args:
config_path: path to file containg configuration.
Raises:
RuntimeError: containig details of the problem.
"""
try:
config = open(config_path, 'r')
except IOError as e:
raise RuntimeError('Opening configuration file {0} failed with: {1!r}'
.format(config_path, e))
try:
parsed_config = json.load(config)
except ValueError as e:
raise RuntimeError(
'Couldn\'t parse configuration file(as JSON): {0!r}'.format(e))
if not isinstance(parsed_config, dict):
raise RuntimeError(
'Config file {0} parsed successfully as JSON. Expected content was a '
'single Object, found: {1!r}.'.format(config_path, parsed_config))
self._parsed_config = parsed_config
self._config_path = config_path
def _RaiseConfigurationFileError(self, key, extra_message):
raise RuntimeError('Error in configuration file {0} in key {1}: {2}'
.format(self._config_path, key, extra_message))
def _GetRawConfigValue(self, key):
if unicode(key) not in self._parsed_config:
self._RaiseConfigurationFileError(key, 'key not found')
return self._parsed_config[unicode(key)]
def GetStringConfigValue(self, key):
value = self._GetRawConfigValue(key)
if not isinstance(value, (str, unicode,)):
self._RaiseConfigurationFileError(
key, 'expected value to be a string but it is {0!r}.'.format(value))
return value
def GetStringListConfigValue(self, key):
"""Returns value for the key if it exists and is a list of strings."""
value = self._GetRawConfigValue(key)
if not isinstance(value, (list)):
self._RaiseConfigurationFileError(
key, 'expected value to be a list but it is {0!r}.'.format(value))
for item in value:
if not isinstance(item, (str, unicode,)):
self._RaiseConfigurationFileError(
key, 'expected all items of the list to be strings but one of them '
'is {0!r}.'.format(item))
return value
def GetExistingFilePath(self, key):
value = self.GetStringConfigValue(key)
if not path.isfile(value):
self._RaiseConfigurationFileError(
key, 'expected value to point to an existing file, file {1!r} does '
'not exist. '.format())
return value
def GetDirListConfigValue(self, key):
value = self.GetStringListConfigValue(key)
for item in value:
if not path.isdir(item):
self._RaiseConfigurationFileError(
key, 'expected value to point to an existing directory, directory '
'{0!r} does not exist.'.format(item))
return value

Binary file not shown.

73
dcj/tool/dcj/dcj.py Normal file
View File

@@ -0,0 +1,73 @@
"""CLI for local testing of solutions in Distributed Code Jam."""
import argparse
import os
from os import chmod
import stat
import subprocess
import sys
from dcj import build
from dcj import command_chooser
from dcj import configuration
from dcj import run
from dcj import test
def _print(x):
print ' '.join(x)
return 0 # Tell tool that command execution was succesfull.
def _subprocess_call_with_error_catching(command):
try:
subprocess.call(command)
return 0
except OSError as e:
if e.args == (2, 'No such file or directory'):
raise ValueError('Command {0} not found.'.format(command[0]))
else:
raise ValueError(
'Error when executing command {0!r}: {1!r}.'.format(command, e))
def _create_script(script_path, content):
with open(script_path, 'w') as f:
f.write(content)
chmod(script_path, stat.S_IRWXU | stat.S_IROTH | stat.S_IXOTH)
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='dcj')
config = configuration.Configuration()
# TODO(jbartosik): allow using different configs.
config.Load(
os.path.join(os.path.dirname(os.path.realpath(__file__)), 'config.json'))
builder = build.Build(config)
runner = run.Run(config)
tester = test.Tester(builder, runner)
chooser = command_chooser.CommandChooser({
'build': builder,
'run': runner,
'test': tester,
})
chooser.AddToParser(parser)
parser.add_argument(
'--dry-run',
action='store_true',
help='Only print commands, don\'t execute them.',
default=False)
args = parser.parse_args()
if args.dry_run:
builder.SetCommandExecutor(_print)
runner.SetCommandExecutor(_print)
else:
builder.SetCommandExecutor(_subprocess_call_with_error_catching)
builder.SetScriptCreator(_create_script)
runner.SetCommandExecutor(_subprocess_call_with_error_catching)
try:
chooser.Run(args)
except (NotImplementedError, RuntimeError, ValueError) as error:
print error
sys.exit(1)

87
dcj/tool/dcj/run.py Normal file
View File

@@ -0,0 +1,87 @@
"""Class for running DCJ test executables."""
import os
from os import path
# First item in the array is default.
_OUTPUT_MODES = ['tagged', 'all', 'files']
class Run(object):
"""A class for running DCJ test executables.
"""
def __init__(self, config):
self._command_executor = lambda x: None
self._config = config
def _AppendToEnvPath(self, path_variable, value_to_append):
if path_variable not in os.environ:
os.environ[path_variable] = ''
os.environ[path_variable] = (
':'.join((os.environ[path_variable], value_to_append)))
def SetCommandExecutor(self, command_executor):
self._command_executor = command_executor
def AddToParser(self, parser):
"""Adds flags to parser and returns it."""
parser.add_argument('--executable', help='path of executable to run.')
parser.add_argument('--nodes', required=True, type=int,
help='number of nodes that will run the solution.')
output = parser.add_argument(
'--output',
default=_OUTPUT_MODES[0]
)
# TOOD(jbartosik): Add line breaks.
output.help = """Mode of output. Allowed values are:
*tagged*::: This is default. In this mode each row of stdout from each
instance of the solution will be prepended with:
STDOUT ${ROW_NUMBER}:
and sent to stdout of the command. Stderr will be treated similarily.
*all*::: In this mode stdout and stderr from all machines will be sent to
(respectively) stdout and stderr of the command.
*files*::: In this mode each row of stdout from each instance of the
solution will written to file
${EXECUTABLE_FILE_NAME}.stdout.${MACHINE_NUMBER}
Stderr will be treated similarily.
"""
return parser
def Run(self, args):
"""Actually run the required executable."""
self._ValidateArgs(args)
parunner = path.join(
os.path.dirname(os.path.realpath(__file__)), '..', 'executable',
self._config.GetStringConfigValue('parunner-file'))
dcj_root = path.join(path.dirname(path.realpath(__file__)), '..')
# TODO(onufry): This modifies the user's env directly, it would be better to
# just pass a modified map of env vars over to the command executor.
self._AppendToEnvPath('PYTHONPATH', path.join(dcj_root, 'libraries'))
self._AppendToEnvPath('PYTHONPATH', path.join(dcj_root, 'modules'))
self._AppendToEnvPath('PATH', '.')
os.environ['LD_LIBRARY_PATH'] = path.join(dcj_root, 'libraries')
if 0 != self._command_executor((parunner,
'--n', str(args['nodes']),
'--stdout', args['output'],
'--stderr', args['output'],
args['executable'])):
raise RuntimeError('Run failed.')
def Description(self):
return 'Runs previously built solution locally.'
def _ValidateArgs(self, args):
"""Validate arguments.
Args:
args: arguments to be validated.
Raises:
ValueError: exception with string describing the problem detected.
"""
if args['nodes'] <= 0:
raise ValueError('argument --nodes must be positive.')
if args['output'] not in _OUTPUT_MODES:
raise ValueError(
'argument --output must be one of ' + ', '.join(_OUTPUT_MODES))

BIN
dcj/tool/dcj/run.pyc Normal file

Binary file not shown.

27
dcj/tool/dcj/test.py Normal file
View File

@@ -0,0 +1,27 @@
"""Class for building & running DCJ test executables."""
class Tester(object):
"""A class for building & running DCJ test executables.
"""
def __init__(self, builder, runner):
self.builder = builder
self.runner = runner
def AddToParser(self, parser):
self.builder.AddToParser(parser)
self.runner.AddToParser(parser)
parser.add_argument('--executable',
help='path of the executable to be built. By default '
'it\'s the same as path of source with removed '
'filename extension.')
return parser
def Run(self, args):
self.builder.Run(args)
args['executable'] = self.builder.ExecutablePath(args)
self.runner.Run(args)
def Description(self):
return 'Builds and locally runs a solution.'

BIN
dcj/tool/dcj/test.pyc Normal file

Binary file not shown.

BIN
dcj/tool/dcj_mac_os.tar.bz Normal file

Binary file not shown.

BIN
dcj/tool/executable/parunner Executable file

Binary file not shown.

View File

@@ -0,0 +1,68 @@
// The contestant-available API for the Zeus distributed contest.
#ifndef MESSAGE_H_ // NOLINT
#define MESSAGE_H_ // NOLINT
#ifdef __cplusplus
extern "C" {
#endif
// The number of nodes on which the solution is running.
int NumberOfNodes();
// The number (in the range [0 .. NumberOfNodes()-1]) of the node on which this
// process is running.
int MyNodeId();
// In all the methods below, if "target" or "source" is not in the valid range,
// the behaviour is undefined.
// The library internally has a message buffer for each of the nodes in
// [0 .. NumberOfNodes()-1]. It accumulates the message in such a buffer through
// the "Put" methods.
// Append "value" to the message that is being prepared for the node with id
// "target".
void PutChar(int target, char value);
void PutInt(int target, int value);
void PutLL(int target, long long value); // NOLINT
// Sends the message that was accumulated in the appropriate buffer to the
// "target" instance, and clear the buffer for this instance.
//
// This method is non-blocking - that is, it does not wait for the receiver to
// call "Receive", it returns immediately after sending the message.
void Send(int target);
// The library also has a receiving buffer for each instance. When you call
// "Receive" and retrieve a message from an instance, the buffer tied to this
// instance is overwritten. You can then retrieve individual parts of the
// message through the Get* methods. You must retrieve the contents of the
// message in the order in which they were appended.
//
// This method is blocking - if there is no message to receive, it will wait for
// the message to arrive.
//
// You can call Receive(-1) to retrieve a message from any source, or with with
// source in [0 .. NumberOfNodes()-1] to retrieve a message from a particular
// source.
//
// It returns the number of the instance which sent the message (which is equal
// to source, unless source is -1).
int Receive(int source);
// Each of these methods returns and consumes one item from the buffer of the
// appropriate instance. You must call these methods in the order in which the
// elements were appended to the message (so, for instance, if the message was
// created with PutChar, PutChar, PutLL, you must call GetChar, GetChar, GetLL
// in this order).
// If you call them in different order, or you call a Get* method after
// consuming all the contents of the buffer, behaviour is undefined.
char GetChar(int source);
int GetInt(int source);
long long GetLL(int source); // NOLINT
#ifdef __cplusplus
}
#endif
#endif // MESSAGE_H_ // NOLINT

72
dcj/tool/includes/zeus.h Normal file
View File

@@ -0,0 +1,72 @@
// Copyright (c) 2014, Onufry Wojtaszczuk <onufryw@gmail.com>
#ifndef RECRUITING_DISTRIBUTED_API_ZEUS_H_
#define RECRUITING_DISTRIBUTED_API_ZEUS_H_
#ifdef __cplusplus
extern "C" {
#endif
#define ZEUS(s) zeus_##s
// The basic contestant-available API for the Zeus distributed contest.
// The number of nodes on which the solution is running.
int ZEUS(NumberOfNodes)();
// The number (in the range [0 .. NumberOfNodes()-1]) of the node on which this
// process is running.
typedef int ZEUS(NodeId);
ZEUS(NodeId) ZEUS(MyNodeId());
// It is guaranteed that messages sent between two given nodes will arrive in
// order.
// No ordering guarantees are given between messages to different nodes (in
// particular, if A sends a message to B, then to C, and C sends a message to B
// after receiving the message from A, it is possible for B to receive C's
// message first).
// Send |bytes| bytes of |message| to node |target|. This is asynchronous (that
// is, it does not wait for the receiver to Receive the message).
// If |message| is shorter than |bytes|, behaviour is undefined.
// If |target| is not a valid node id (not in [0 .. NumberOfNodes()-1]), will
// crash.
void ZEUS(Send)(ZEUS(NodeId) target, const char *message, int bytes);
typedef struct {
// Id of the sending node.
ZEUS(NodeId) sender_id;
// Length of the received message in bytes.
int length;
} ZEUS(MessageInfo);
// Receive a message from the |source| node id, and copy the contents of the
// message to |buffer|, up to |buffer_size| bytes. No extra characters (in
// particular, a trailing '\0') will be appended to the message.
// A special case is |source| equal to -1, in which case a message from any
// other node can be received.
// This call is blocking - that is, the function will not return until a message
// is received.
// A std::pair will be returned.
// First element of the pair will be the id of the sending node.
// Second element of the pair will be the length of the received message
// in bytes.
//
// If the received message is larger than |buffer_size|, will crash.
// If the received message is smaller than |buffer_size|, the rest of the buffer
// contents are not modified.
// If |buffer| is smaller than |buffer_size|, behaviour is undefined.
// If |source| is neither -1 nor a valid node ID, will crash.
ZEUS(MessageInfo) ZEUS(Receive)(ZEUS(NodeId) source, char *buffer, int buffer_size);
// Returns the list of nodes from which we have unreceived messages (thus,
// calling Receive() with one of the returned node ids as the argument will
// not block). The order in which the node IDs are given is not specified. Each
// ID will be given once.
//std::vector<NodeId> Poll(); // NOTE: NOT IMPLEMENTED
#ifdef __cplusplus
}
#endif
#endif // RECRUITING_DISTRIBUTED_API_ZEUS_H_

Binary file not shown.

View File

@@ -0,0 +1,6 @@
class Wrapper {
public static void main(String[] args) {
System.loadLibrary("message");
Main.main(args);
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,51 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 2.0.11
*
* Do not make changes to this file unless you know what you are doing--modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
public class message {
public static int NumberOfNodes() {
return messageJNI.NumberOfNodes();
}
public static int MyNodeId() {
return messageJNI.MyNodeId();
}
public static void PutChar(int target, char value) {
messageJNI.PutChar(target, value);
}
public static void PutInt(int target, int value) {
messageJNI.PutInt(target, value);
}
public static void PutLL(int target, long value) {
messageJNI.PutLL(target, value);
}
public static void Send(int target) {
messageJNI.Send(target);
}
public static int Receive(int source) {
return messageJNI.Receive(source);
}
public static char GetChar(int source) {
return messageJNI.GetChar(source);
}
public static int GetInt(int source) {
return messageJNI.GetInt(source);
}
public static long GetLL(int source) {
return messageJNI.GetLL(source);
}
}

Binary file not shown.

View File

@@ -0,0 +1,21 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 2.0.11
*
* Do not make changes to this file unless you know what you are doing--modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
public class messageJNI {
public final static native int NumberOfNodes();
public final static native int MyNodeId();
public final static native void PutChar(int jarg1, char jarg2);
public final static native void PutInt(int jarg1, int jarg2);
public final static native void PutLL(int jarg1, long jarg2);
public final static native void Send(int jarg1);
public final static native int Receive(int jarg1);
public final static native char GetChar(int jarg1);
public final static native int GetInt(int jarg1);
public final static native long GetLL(int jarg1);
}

View File

@@ -0,0 +1,158 @@
#include "message.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "zeus.h"
#define ZEUS_WRAP(s) zeus_##s
#define DEBUG 1
#define MAX_MESSAGE_SIZE (8 * (1 << 20))
#define MAX_MACHINES 100
static void Die(const char* s) {
fputs(s, stderr);
exit(20);
}
int NumberOfNodes() { return ZEUS_WRAP(NumberOfNodes)(); }
int MyNodeId() { return ZEUS_WRAP(MyNodeId)(); }
static void CheckNodeId(int node) {
if (!DEBUG) return;
if (node < 0 || node >= NumberOfNodes()) Die("Incorrect machine number");
}
typedef struct Buffer {
char* buffer;
int size;
int pos; // for input buffers next byte to be read. for output buffers next
// byte to be written.
} Buffer;
static int Empty(Buffer* buffer) { return buffer->pos >= buffer->size; }
static unsigned char GetRawByte(Buffer* buf) {
if (Empty(buf)) {
Die("Read past the end of the message");
}
char r = buf->buffer[buf->pos++];
if (Empty(buf)) {
free(buf->buffer);
buf->buffer = NULL;
buf->pos = 0;
buf->size = 0;
}
return r;
}
static void PutRawByte(Buffer* buffer, unsigned char byte) {
if (buffer->pos >= buffer->size) {
buffer->size = 2 * buffer->size;
if (buffer->size < 128) buffer->size = 128;
buffer->buffer = (char*)realloc(buffer->buffer, buffer->size);
assert(buffer->buffer);
}
buffer->buffer[buffer->pos++] = byte;
}
static Buffer incoming_buffers[MAX_MACHINES];
static Buffer outgoing_buffers[MAX_MACHINES];
char recv_buffer[MAX_MESSAGE_SIZE];
int Receive(int source) {
if (source != -1) CheckNodeId(source);
if (DEBUG && source == -1) {
int i;
for (i = 0; i < ZEUS_WRAP(NumberOfNodes)(); i++) {
if (!Empty(&incoming_buffers[i]))
Die("Cannot call Receive(-1) if any message is unread.");
}
}
ZEUS_WRAP(MessageInfo) mi =
ZEUS_WRAP(Receive)(source, recv_buffer, sizeof(recv_buffer));
Buffer* buf = &incoming_buffers[mi.sender_id];
if (DEBUG && !Empty(buf))
Die("Receive()'ed a message when the previous one wasn't consumed.");
if (buf->buffer != NULL) {
free(buf->buffer);
buf->buffer = NULL;
}
buf->buffer = (char*)malloc(mi.length);
assert(buf->buffer);
memcpy(buf->buffer, recv_buffer, mi.length);
buf->pos = 0;
buf->size = mi.length;
return mi.sender_id;
}
#define kChar 14
#define kInt 15
#define kLL 16
static void GetTag(int source, int expected) {
if (!DEBUG) return;
int tag = GetRawByte(&incoming_buffers[source]);
if (tag != expected) Die("Type mismatch between read and requested types");
}
int GetInt(int source) {
CheckNodeId(source);
GetTag(source, kInt);
int result = 0, i;
for (i = 0; i < 4; i++)
result |= (int)(GetRawByte(&incoming_buffers[source])) << (8 * i);
return result;
}
void PutInt(int target, int value) {
CheckNodeId(target);
if (DEBUG) PutRawByte(&outgoing_buffers[target], kInt);
int i;
for (i = 0; i < 4; i++)
PutRawByte(&outgoing_buffers[target], (0xff & (value >> (8 * i))));
}
long long GetLL(int source) {
CheckNodeId(source);
GetTag(source, kLL);
long long result = 0;
int i;
for (i = 0; i < 8; i++)
result |= (long long)(GetRawByte(&incoming_buffers[source])) << (8 * i);
return result;
}
void PutLL(int target, long long value) {
CheckNodeId(target);
if (DEBUG) PutRawByte(&outgoing_buffers[target], kLL);
int i;
for (i = 0; i < 8; i++)
PutRawByte(&outgoing_buffers[target], (0xff & (value >> (8 * i))));
}
char GetChar(int source) {
CheckNodeId(source);
GetTag(source, kChar);
return GetRawByte(&incoming_buffers[source]);
}
void PutChar(int target, char value) {
CheckNodeId(target);
if (DEBUG) PutRawByte(&outgoing_buffers[target], kChar);
PutRawByte(&outgoing_buffers[target], value);
}
void Send(int target) {
CheckNodeId(target);
Buffer* buffer = &outgoing_buffers[target];
if (buffer->pos > (int)sizeof(recv_buffer)) Die("Message too long");
ZEUS_WRAP(Send)(target, buffer->buffer, buffer->pos);
free(buffer->buffer);
buffer->buffer = NULL;
buffer->pos = buffer->size = 0;
}

Binary file not shown.

View File

@@ -0,0 +1,335 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 2.0.11
*
* This file is not intended to be easily readable and contains a number of
* coding conventions designed to improve portability and efficiency. Do not make
* changes to this file unless you know what you are doing--modify the SWIG
* interface file instead.
* ----------------------------------------------------------------------------- */
#define SWIGJAVA
/* -----------------------------------------------------------------------------
* This section contains generic SWIG labels for method/variable
* declarations/attributes, and other compiler dependent labels.
* ----------------------------------------------------------------------------- */
/* template workaround for compilers that cannot correctly implement the C++ standard */
#ifndef SWIGTEMPLATEDISAMBIGUATOR
# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
# define SWIGTEMPLATEDISAMBIGUATOR template
# elif defined(__HP_aCC)
/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
# define SWIGTEMPLATEDISAMBIGUATOR template
# else
# define SWIGTEMPLATEDISAMBIGUATOR
# endif
#endif
/* inline attribute */
#ifndef SWIGINLINE
# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
# define SWIGINLINE inline
# else
# define SWIGINLINE
# endif
#endif
/* attribute recognised by some compilers to avoid 'unused' warnings */
#ifndef SWIGUNUSED
# if defined(__GNUC__)
# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
# define SWIGUNUSED __attribute__ ((__unused__))
# else
# define SWIGUNUSED
# endif
# elif defined(__ICC)
# define SWIGUNUSED __attribute__ ((__unused__))
# else
# define SWIGUNUSED
# endif
#endif
#ifndef SWIG_MSC_UNSUPPRESS_4505
# if defined(_MSC_VER)
# pragma warning(disable : 4505) /* unreferenced local function has been removed */
# endif
#endif
#ifndef SWIGUNUSEDPARM
# ifdef __cplusplus
# define SWIGUNUSEDPARM(p)
# else
# define SWIGUNUSEDPARM(p) p SWIGUNUSED
# endif
#endif
/* internal SWIG method */
#ifndef SWIGINTERN
# define SWIGINTERN static SWIGUNUSED
#endif
/* internal inline SWIG method */
#ifndef SWIGINTERNINLINE
# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
#endif
/* exporting methods */
#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
# ifndef GCC_HASCLASSVISIBILITY
# define GCC_HASCLASSVISIBILITY
# endif
#endif
#ifndef SWIGEXPORT
# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
# if defined(STATIC_LINKED)
# define SWIGEXPORT
# else
# define SWIGEXPORT __declspec(dllexport)
# endif
# else
# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
# define SWIGEXPORT __attribute__ ((visibility("default")))
# else
# define SWIGEXPORT
# endif
# endif
#endif
/* calling conventions for Windows */
#ifndef SWIGSTDCALL
# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
# define SWIGSTDCALL __stdcall
# else
# define SWIGSTDCALL
# endif
#endif
/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
# define _CRT_SECURE_NO_DEPRECATE
#endif
/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
# define _SCL_SECURE_NO_DEPRECATE
#endif
/* Fix for jlong on some versions of gcc on Windows */
#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
typedef long long __int64;
#endif
/* Fix for jlong on 64-bit x86 Solaris */
#if defined(__x86_64)
# ifdef _LP64
# undef _LP64
# endif
#endif
#include <jni.h>
#include <stdlib.h>
#include <string.h>
/* Support for throwing Java exceptions */
typedef enum {
SWIG_JavaOutOfMemoryError = 1,
SWIG_JavaIOException,
SWIG_JavaRuntimeException,
SWIG_JavaIndexOutOfBoundsException,
SWIG_JavaArithmeticException,
SWIG_JavaIllegalArgumentException,
SWIG_JavaNullPointerException,
SWIG_JavaDirectorPureVirtual,
SWIG_JavaUnknownError
} SWIG_JavaExceptionCodes;
typedef struct {
SWIG_JavaExceptionCodes code;
const char *java_exception;
} SWIG_JavaExceptions_t;
static void SWIGUNUSED SWIG_JavaThrowException(JNIEnv *jenv, SWIG_JavaExceptionCodes code, const char *msg) {
jclass excep;
static const SWIG_JavaExceptions_t java_exceptions[] = {
{ SWIG_JavaOutOfMemoryError, "java/lang/OutOfMemoryError" },
{ SWIG_JavaIOException, "java/io/IOException" },
{ SWIG_JavaRuntimeException, "java/lang/RuntimeException" },
{ SWIG_JavaIndexOutOfBoundsException, "java/lang/IndexOutOfBoundsException" },
{ SWIG_JavaArithmeticException, "java/lang/ArithmeticException" },
{ SWIG_JavaIllegalArgumentException, "java/lang/IllegalArgumentException" },
{ SWIG_JavaNullPointerException, "java/lang/NullPointerException" },
{ SWIG_JavaDirectorPureVirtual, "java/lang/RuntimeException" },
{ SWIG_JavaUnknownError, "java/lang/UnknownError" },
{ (SWIG_JavaExceptionCodes)0, "java/lang/UnknownError" }
};
const SWIG_JavaExceptions_t *except_ptr = java_exceptions;
while (except_ptr->code != code && except_ptr->code)
except_ptr++;
(*jenv)->ExceptionClear(jenv);
excep = (*jenv)->FindClass(jenv, except_ptr->java_exception);
if (excep)
(*jenv)->ThrowNew(jenv, excep, msg);
}
/* Contract support */
#define SWIG_contract_assert(nullreturn, expr, msg) if (!(expr)) {SWIG_JavaThrowException(jenv, SWIG_JavaIllegalArgumentException, msg); return nullreturn; } else
extern int NumberOfNodes();
extern int MyNodeId();
extern void PutChar(int target, char value);
extern void PutInt(int target, int value);
extern void PutLL(int target, long long value);
extern void Send(int target);
extern int Receive(int source);
extern char GetChar(int source);
extern int GetInt(int source);
extern long long GetLL(int source);
#ifdef __cplusplus
extern "C" {
#endif
SWIGEXPORT jint JNICALL Java_messageJNI_NumberOfNodes(JNIEnv *jenv, jclass jcls) {
jint jresult = 0 ;
int result;
(void)jenv;
(void)jcls;
result = (int)NumberOfNodes();
jresult = (jint)result;
return jresult;
}
SWIGEXPORT jint JNICALL Java_messageJNI_MyNodeId(JNIEnv *jenv, jclass jcls) {
jint jresult = 0 ;
int result;
(void)jenv;
(void)jcls;
result = (int)MyNodeId();
jresult = (jint)result;
return jresult;
}
SWIGEXPORT void JNICALL Java_messageJNI_PutChar(JNIEnv *jenv, jclass jcls, jint jarg1, jchar jarg2) {
int arg1 ;
char arg2 ;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
arg2 = (char)jarg2;
PutChar(arg1,arg2);
}
SWIGEXPORT void JNICALL Java_messageJNI_PutInt(JNIEnv *jenv, jclass jcls, jint jarg1, jint jarg2) {
int arg1 ;
int arg2 ;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
arg2 = (int)jarg2;
PutInt(arg1,arg2);
}
SWIGEXPORT void JNICALL Java_messageJNI_PutLL(JNIEnv *jenv, jclass jcls, jint jarg1, jlong jarg2) {
int arg1 ;
long long arg2 ;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
arg2 = (long long)jarg2;
PutLL(arg1,arg2);
}
SWIGEXPORT void JNICALL Java_messageJNI_Send(JNIEnv *jenv, jclass jcls, jint jarg1) {
int arg1 ;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
Send(arg1);
}
SWIGEXPORT jint JNICALL Java_messageJNI_Receive(JNIEnv *jenv, jclass jcls, jint jarg1) {
jint jresult = 0 ;
int arg1 ;
int result;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
result = (int)Receive(arg1);
jresult = (jint)result;
return jresult;
}
SWIGEXPORT jchar JNICALL Java_messageJNI_GetChar(JNIEnv *jenv, jclass jcls, jint jarg1) {
jchar jresult = 0 ;
int arg1 ;
char result;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
result = (char)GetChar(arg1);
jresult = (jchar)result;
return jresult;
}
SWIGEXPORT jint JNICALL Java_messageJNI_GetInt(JNIEnv *jenv, jclass jcls, jint jarg1) {
jint jresult = 0 ;
int arg1 ;
int result;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
result = (int)GetInt(arg1);
jresult = (jint)result;
return jresult;
}
SWIGEXPORT jlong JNICALL Java_messageJNI_GetLL(JNIEnv *jenv, jclass jcls, jint jarg1) {
jlong jresult = 0 ;
int arg1 ;
long long result;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
result = (long long)GetLL(arg1);
jresult = (jlong)result;
return jresult;
}
#ifdef __cplusplus
}
#endif

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,147 @@
#include "zeus.h"
#include <assert.h>
#include <stdio.h>
#include <time.h>
#ifdef WIN32
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <stdlib.h>
#endif
#define MAX_MESSAGE_SIZE (8*1024*1024)
#define MAGIC 1736434764
#define SEND 3
#define RECV 4
static int initialized;
static FILE* cmdin;
static FILE* cmdout;
static int nof_nodes;
static int node_id;
static unsigned char ReadByte() {
unsigned char c;
assert(fread(&c, 1, 1, cmdin) == 1);
return c;
}
static int ReadInt() {
int v = 0;
int i;
for(i=0;i<4;i++)
v |= (int)(ReadByte()) << (8 * i);
return v;
}
static void WriteByte(unsigned char c) {
assert(fwrite(&c, 1, 1, cmdout) == 1);
}
static void WriteInt(int v) {
int i;
for(i=0;i<4;i++)
WriteByte((v >> (8 * i)) & 0xff);
}
#ifdef WIN32
static int GetFd(int dir) {
const char* names[2] = { "ZSHANDLE_IN", "ZSHANDLE_OUT" };
char* handle_s = getenv(names[dir]);
if (handle_s == NULL)
return -1;
int handle = atoi(handle_s);
return _open_osfhandle(handle, dir == 0 ? _O_RDONLY : _O_APPEND);
}
#else
static int GetFd(int dir) {
return 3 + dir;
}
#endif
static void Init() {
if (initialized)
return;
cmdin = fdopen(GetFd(0), "r");
assert(cmdin != NULL);
cmdout = fdopen(GetFd(1), "w");
assert(cmdout != NULL);
if (ReadInt() != MAGIC)
assert(0);
nof_nodes = ReadInt();
assert(1 <= nof_nodes);
node_id = ReadInt();
assert(0 <= node_id && node_id < nof_nodes);
initialized = 1;
}
int ZEUS(NumberOfNodes)() {
Init();
return nof_nodes;
}
ZEUS(NodeId) ZEUS(MyNodeId)() {
Init();
return node_id;
}
#ifdef WIN32
static int CurrentTime() {
HANDLE me = GetCurrentProcess();
FILETIME lpCreationTime, lpExitTime, lpKernelTime, lpUserTime;
GetProcessTimes(me, &lpCreationTime, &lpExitTime, &lpKernelTime, &lpUserTime);
ULONGLONG cTime =
lpUserTime.dwLowDateTime +
lpKernelTime.dwLowDateTime +
(((ULONGLONG) lpUserTime.dwHighDateTime) << 32) +
(((ULONGLONG) lpKernelTime.dwHighDateTime) << 32);
return (int)(cTime / 10000);
}
#else
static int CurrentTime() {
static int warned;
int time = clock();
if (time == -1) {
if (!warned) {
warned = 1;
fprintf(stderr, "Warning: clock() returned -1; time measurements will be bogus.\n");
}
return 0;
}
return time * 1000 / CLOCKS_PER_SEC;
}
#endif
void ZEUS(Send)(ZEUS(NodeId) target, const char* message, int bytes) {
Init();
assert(target >= 0 && target < nof_nodes);
assert(bytes <= MAX_MESSAGE_SIZE);
int i;
WriteByte(SEND);
WriteInt(target);
WriteInt(CurrentTime());
WriteInt(bytes);
for(i=0;i<bytes;i++)
WriteByte(message[i]);
fflush(cmdout);
}
ZEUS(MessageInfo) ZEUS(Receive)(ZEUS(NodeId) source, char* buffer, int buffer_size) {
Init();
assert(source >= -1 && source < nof_nodes);
ZEUS(MessageInfo) mi;
int i;
WriteByte(RECV);
WriteInt(source);
WriteInt(CurrentTime());
fflush(cmdout);
if (ReadInt() != MAGIC + 1)
assert(0);
mi.sender_id = ReadInt();
mi.length = ReadInt();
assert(mi.length <= buffer_size);
for(i=0;i<mi.length;i++)
buffer[i] = ReadByte();
return mi;
}

Binary file not shown.

114
dcj/tool/modules/message.py Normal file
View File

@@ -0,0 +1,114 @@
# This file was automatically generated by SWIG (http://www.swig.org).
# Version 2.0.11
#
# Do not make changes to this file unless you know what you are doing--modify
# the SWIG interface file instead.
from sys import version_info
if version_info >= (2,6,0):
def swig_import_helper():
from os.path import dirname
import imp
fp = None
try:
fp, pathname, description = imp.find_module('_message', [dirname(__file__)])
except ImportError:
import _message
return _message
if fp is not None:
try:
_mod = imp.load_module('_message', fp, pathname, description)
finally:
fp.close()
return _mod
_message = swig_import_helper()
del swig_import_helper
else:
import _message
del version_info
try:
_swig_property = property
except NameError:
pass # Python < 2.2 doesn't have 'property'.
def _swig_setattr_nondynamic(self,class_type,name,value,static=1):
if (name == "thisown"): return self.this.own(value)
if (name == "this"):
if type(value).__name__ == 'SwigPyObject':
self.__dict__[name] = value
return
method = class_type.__swig_setmethods__.get(name,None)
if method: return method(self,value)
if (not static):
self.__dict__[name] = value
else:
raise AttributeError("You cannot add attributes to %s" % self)
def _swig_setattr(self,class_type,name,value):
return _swig_setattr_nondynamic(self,class_type,name,value,0)
def _swig_getattr(self,class_type,name):
if (name == "thisown"): return self.this.own()
method = class_type.__swig_getmethods__.get(name,None)
if method: return method(self)
raise AttributeError(name)
def _swig_repr(self):
try: strthis = "proxy of " + self.this.__repr__()
except: strthis = ""
return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
try:
_object = object
_newclass = 1
except AttributeError:
class _object : pass
_newclass = 0
def NumberOfNodes():
return _message.NumberOfNodes()
NumberOfNodes = _message.NumberOfNodes
def MyNodeId():
return _message.MyNodeId()
MyNodeId = _message.MyNodeId
def PutChar(*args):
return _message.PutChar(*args)
PutChar = _message.PutChar
def PutInt(*args):
return _message.PutInt(*args)
PutInt = _message.PutInt
def PutLL(*args):
return _message.PutLL(*args)
PutLL = _message.PutLL
def Send(*args):
return _message.Send(*args)
Send = _message.Send
def Receive(*args):
return _message.Receive(*args)
Receive = _message.Receive
def GetChar(*args):
return _message.GetChar(*args)
GetChar = _message.GetChar
def GetInt(*args):
return _message.GetInt(*args)
GetInt = _message.GetInt
def GetLL(*args):
return _message.GetLL(*args)
GetLL = _message.GetLL
# This file is compatible with both classic and new-style classes.

View File

@@ -0,0 +1,27 @@
{
"c-compiler": "gcc",
"c-compiler-flags": [
"-O2",
"-lm",
"-static"
],
"cpp-compiler": "g++",
"cpp-compiler-flags": [
"-O2",
"-std=gnu++0x",
"-lm",
"-static"
],
"java-compiler": "javac",
"java-compiler-classpath-arg": "-classpath",
"java-include-dirs": [
"/usr/lib/jvm/java-7-openjdk-amd64/include"
],
"java-native-library-linker": "ld",
"java-native-library-linker-options": [
"-shared"
],
"java-native-library-name": "libmessage.so",
"java-wrapper-file-content": "#!/bin/bash\ncd {0}\n/usr/bin/java -classpath {1} Wrapper\n",
"parunner-file": "parunner"
}

View File

@@ -0,0 +1,27 @@
{
"c-compiler": "gcc",
"c-compiler-flags": [
"-O2",
"-lm"
],
"cpp-compiler": "g++",
"cpp-compiler-flags": [
"-O2",
"-std=gnu++0x",
"-lm"
],
"java-compiler": "javac",
"java-compiler-classpath-arg": "-classpath",
"java-include-dirs": [
"/System//Library/Frameworks/JavaVM.framework/Versions/A/Headers"
],
"java-native-library-linker": "cc",
"java-native-library-linker-options": [
"-framework",
"JavaVM",
"-bundle"
],
"java-native-library-name": "libmessage.jnilib",
"java-wrapper-file-content": "#!/bin/bash\ncd {0}\n/usr/bin/java -Djava.library.path={1} -classpath {1} Wrapper\n",
"parunner-file": "parunner"
}

View File

@@ -0,0 +1,16 @@
{
"c-compiler": "gcc",
"c-compiler-flags": [
"-O2",
"-lm",
"-static"
],
"cpp-compiler": "g++",
"cpp-compiler-flags": [
"-O2",
"-std=gnu++0x",
"-lm",
"-static"
],
"parunner-file": "parunner.exe"
}

View File

@@ -0,0 +1,74 @@
# Description:
# Auto-imported from github.com/robryk/parunner
package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # BSD 3-clause
exports_files(["LICENSE"])
go_binary(
name = "parunner",
srcs = [
"binaries_test_unix.go",
"binaries_test_windows.go",
"comm.go",
"filepipe.go",
"instance.go",
"instance_unix.go",
"instance_windows.go",
"instances.go",
"main.go",
"route.go",
"util.go",
],
)
go_test(
name = "filepipe_test",
size = "small",
srcs = ["filepipe_test.go"],
library = ":parunner",
)
go_test(
name = "instance_test",
size = "small",
srcs = [
"google_init_test.go",
"instance_test.go",
],
data = [
"//third_party/golang/parunner/zeus:hanger",
"//third_party/golang/parunner/zeus:tester",
],
library = ":parunner",
)
go_test(
name = "instances_test",
size = "small",
srcs = [
"google_init_test.go",
"instances_test.go",
],
data = [
"//third_party/golang/parunner/zeus:hanger",
"//third_party/golang/parunner/zeus:tester",
],
library = ":parunner",
)
go_test(
name = "route_test",
size = "small",
srcs = ["route_test.go"],
library = ":parunner",
)
go_test(
name = "util_test",
size = "small",
srcs = ["util_test.go"],
library = ":parunner",
)

View File

@@ -0,0 +1,12 @@
Copyright (c) 2014, Robert Obryk <robryk@gmail.com> and others
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,12 @@
Copyright (c) 2014, Robert Obryk <robryk@gmail.com> and others
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,3 @@
robryk
jbartosik

View File

@@ -0,0 +1,16 @@
URL: https://github.com/robryk/parunner/archive/32a202bb14a55b39ed8031836c5fa9f776744594.zip
Version: 32a202bb14a55b39ed8031836c5fa9f776744594
License: BSD 3-clause
License File: LICENSE
Description:
Utility that can run submissions to distributed programming contests ran on zeus
(//recruiting/distributed/zeus) on a single machine.
Local Modifications:
Automated import rewriting by //third_party/golang/update.go.
Copied COPYING to LICENSE.
Merged incorrectly split go_binary rules in BUILD.
Added cc_{library,binary} BUILD rules in zeus/BUILD.
Added google_init_test.go to the directory and to go_test rules.
Added data dependencies to go_test rules.

View File

@@ -0,0 +1,40 @@
parunner
========
Single-machine runner for [distributed](http://potyczki.mimuw.edu.pl/l/zadania_rozproszone/) [Potyczki Algorytmiczne](http://potyczki.mimuw.edu.pl/) problems ([a](https://sio2.mimuw.edu.pl/pa/c/pa-2014-1/p/mak/) [few](https://sio2.mimuw.edu.pl/pa/c/pa-2014-1/p/kol/) [examples](https://sio2.mimuw.edu.pl/pa/c/pa-2014-1/p/sek/)).
[![Build Status](https://drone.io/github.com/robryk/parunner/status.png)](https://drone.io/github.com/robryk/parunner/latest) [![GoDoc](https://godoc.org/github.com/robryk/parunner?status.png)](https://godoc.org/github.com/robryk/parunner)
Usage
-----
In order to run a program that uses [raw zeus interface](https://github.com/robryk/parunner/blob/master/zeus/zeus.h), you need to link it with [zeus/zeus_local.c](https://github.com/robryk/parunner/blob/master/zeus/zeus_local.c) instead of any other implementation of zeus_local. You can then run the program as follows:
$ parunner -n=number_of_instances path/to/program
There is an [example](https://github.com/robryk/parunner/blob/master/zeus/example.c) provided. In order to run it, you should:
1. Compile it: `make -C zeus example`
2. Obtain a binary of parunner. If you have a Go toolchain installed, you can compile it by doing `go get github.com/robryk/parunner`. The binary will then be built and written to `$GOPATH/bin/parunner`. There is also a compiled binary for [linux-amd64](https://drone.io/github.com/robryk/parunner/files/parunner) available.
3. Run `parunner -n=3 -trace_comm -stdout=tagged zeus/example`. The output should look like this:
```
robryk@sharya-rana ~/g/s/g/r/parunner> parunner -n=3 -trace_comm -stdout=tagged zeus/example
STDOUT 0: Nodeow jest 3, a ja mam numer 0.
STDOUT 0: Wysylam wiadomosc do 1.
STDOUT 1: Nodeow jest 3, a ja mam numer 1.
STDOUT 1: Wysylam wiadomosc do 2.
STDOUT 1: Odbieram wiadomosc od 0.
STDOUT 2: Nodeow jest 3, a ja mam numer 2.
STDOUT 2: Odbieram wiadomosc od 1.
COMM: instancja 1:instancja 0 wysyła do mnie wiadomość (13 bajtów) [0]
COMM: instancja 2:instancja 1 wysyła do mnie wiadomość (13 bajtów) [0]
COMM: instancja 1:czekam na wiadomość od instancji 0 [0]
COMM: instancja 1:odebrałam wiadomość od instancji 0 (13 bajtów)
STDOUT 1: Odebralem: Hello from 0!
COMM: instancja 2:czekam na wiadomość od instancji 1 [0]
COMM: instancja 2:odebrałam wiadomość od instancji 1 (13 bajtów)
STDOUT 2: Odebralem: Hello from 1!
Czas trwania: 0 (najdłużej działająca instancja: 2)
```
For more information on parunner's usage invoke it with no arguments.

View File

@@ -0,0 +1,6 @@
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package main
const testerPath = "zeus/tester"
const hangerPath = "zeus/hanger"

View File

@@ -0,0 +1,4 @@
package main
const testerPath = "zeus\\tester.exe"
const hangerPath = "zeus\\hanger.exe"

View File

@@ -0,0 +1,218 @@
package main
import (
"encoding/binary"
"flag"
"fmt"
"io"
"time"
)
const magic = 1736434764
const recvResponseMagic = magic + 1
const sendOpType = 3
const recvOpType = 4
type header struct {
Magic uint32
NodeCount int32
NodeID int32
}
type recvResponse struct {
RecvResponseMagic uint32
SourceID int32
Length int32
// Message []byte
}
type recvHeader struct {
// OpType byte
SourceID int32
Time int32 // milliseconds
}
type sendHeader struct {
// OpType byte
TargetID int32
Time int32 // milliseconds
Length int32
// Message []byte
}
// TODO(robryk): Move this to instance-creation-time options
var messageCountLimit = flag.Int("message_count_limit", 1000, "Limit for the number of messages sent per instance")
var messageSizeLimit = flag.Int("message_size_limit", 8*1024*1024, "Limit for the total size of messages sent by an instance, in bytes")
type Message struct {
Source int
Target int
SendTime time.Duration
Message []byte
}
// ErrMessageCount is returned when an instance exceeds the per-instance message count limit.
// It is usually encapsulated in an InstanceError that specifies the instance ID.
type ErrMessageCount struct {
}
func (err ErrMessageCount) Error() string {
return fmt.Sprintf("sent message count limit (%d) exceeded", *messageCountLimit)
}
// ErrMessageSize is returned when an instance exceeds the per-instance total messages size limit.
// It is usually encapsulated in an InstanceError that specifies the instance ID.
type ErrMessageSize struct {
}
func (err ErrMessageSize) Error() string {
return fmt.Sprintf("total sent message size limit (%d bytes) exceeded", *messageSizeLimit)
}
func writeMessage(w io.Writer, message *Message) error {
rr := recvResponse{
RecvResponseMagic: recvResponseMagic,
SourceID: int32(message.Source),
Length: int32(len(message.Message)),
}
if err := binary.Write(w, binary.LittleEndian, &rr); err != nil {
return err
}
if n, err := w.Write(message.Message); n < len(message.Message) {
if err == nil {
err = io.ErrShortWrite
}
return err
}
return nil
}
func writeHeader(w io.Writer, id int, instanceCount int) error {
h := header{
Magic: magic,
NodeCount: int32(instanceCount),
NodeID: int32(id),
}
return binary.Write(w, binary.LittleEndian, &h)
}
const (
requestSend = iota
requestRecv
requestRecvAny
// requestNop
)
type request struct {
requestType int
time time.Duration
// for requestSend:
destination int
message []byte
// for requestRecv:
source int
}
func (req request) hasResponse() bool {
switch req.requestType {
case requestRecv:
return true
case requestRecvAny:
return true
default:
return false
}
}
type response struct {
message *Message
}
func readRequest(r io.Reader) (*request, error) {
var opType [1]byte
if _, err := r.Read(opType[:]); err != nil {
return nil, err
}
switch opType[0] {
case sendOpType:
var sh sendHeader
if err := binary.Read(r, binary.LittleEndian, &sh); err != nil {
return nil, err
}
if sh.Length < 0 || int(sh.Length) > *messageSizeLimit {
return nil, fmt.Errorf("invalid size of a message to be sent: %d", sh.Length)
}
if sh.TargetID < 0 || sh.TargetID >= MaxInstances {
return nil, fmt.Errorf("invalid target instance in a send request: %d", sh.TargetID)
}
message := make([]byte, sh.Length)
if _, err := io.ReadFull(r, message); err != nil {
return nil, err
}
return &request{
requestType: requestSend,
time: time.Duration(sh.Time) * time.Millisecond,
destination: int(sh.TargetID),
message: message}, nil
case recvOpType:
var rh recvHeader
if err := binary.Read(r, binary.LittleEndian, &rh); err != nil {
return nil, err
}
if rh.SourceID < -1 || rh.SourceID >= MaxInstances {
return nil, fmt.Errorf("invalid source instance in a receive request: %d", rh.SourceID)
}
if rh.SourceID == -1 {
return &request{requestType: requestRecvAny, time: time.Duration(rh.Time) * time.Millisecond}, nil
} else {
return &request{requestType: requestRecv, time: time.Duration(rh.Time) * time.Millisecond, source: int(rh.SourceID)}, nil
}
default:
return nil, fmt.Errorf("invalid operation type 0x%x", opType[0])
}
}
func (i *Instance) communicate(r io.Reader, w io.Writer, reqCh chan<- *request, respCh <-chan *response) error {
i.TimeBlocked = time.Duration(0)
// TODO: Figure out what errors should be returned from this function. We currently error if the instance fails to read the header (which is mitigated by delaying the closure of other ends of the pipes), for example.
if err := writeHeader(w, i.ID, i.TotalInstances); err != nil {
return err
}
for {
req, err := readRequest(r)
if err != nil {
if err == io.EOF {
//return nil
}
return err
}
req.time += i.TimeBlocked
if req.requestType == requestSend {
i.MessagesSent++
if i.MessagesSent > *messageCountLimit {
return ErrMessageCount{}
}
i.MessageBytesSent += len(req.message)
if i.MessageBytesSent > *messageSizeLimit {
return ErrMessageSize{}
}
}
currentTime := req.time
hasResponse := req.hasResponse()
reqCh <- req
if hasResponse {
resp, ok := <-respCh
if !ok {
return fmt.Errorf("Received no response for a receive request")
}
if resp.message.SendTime > currentTime {
i.TimeBlocked += resp.message.SendTime - currentTime
}
if err := writeMessage(w, resp.message); err != nil {
return err
}
}
}
}

View File

@@ -0,0 +1,109 @@
package main
import (
"errors"
"io"
"io/ioutil"
"os"
"runtime"
"sync"
)
// FilePipe is a tailable buffer backed by a file.
type FilePipe struct {
f *os.File
mu sync.Mutex
cond sync.Cond
size int64
closing bool
}
// Create a new FilePipe backed by a temporary file.
func NewFilePipe() (*FilePipe, error) {
// On some OSes we could remove the file immediately.
f, err := ioutil.TempFile("", "filepipe")
if err != nil {
return nil, err
}
fp := &FilePipe{f: f}
fp.cond.L = &fp.mu
runtime.SetFinalizer(fp, (*FilePipe).Release)
return fp, nil
}
// Release releases the resources associated with the filepipe. In particular,
// it removes the backing file. No readers should be used concurrently with a
// call to Release, nor after a call to Release. Release is idempotent.
func (fp *FilePipe) Release() error {
fp.Close()
if fp.f == nil {
return nil
}
filename := fp.f.Name()
err := fp.f.Close()
if err1 := os.Remove(filename); err == nil {
err = err1
}
fp.f = nil
return err
}
// Reader creates a new reader that starts reading from the beginning of
// the filepipe's contents and blocks at the end until the filepipe is closed.
func (fp *FilePipe) Reader() io.Reader {
return &filePipeReader{fp: fp, pos: 0}
}
func (fp *FilePipe) Write(buf []byte) (int, error) {
n, err := fp.f.Write(buf)
fp.mu.Lock()
defer fp.mu.Unlock()
if fp.closing {
return 0, errors.New("write to a closed filepipe")
}
fp.size += int64(n)
fp.cond.Broadcast()
return n, err
}
// Close finalizes the filepipe's contents. Once Close is called, all readers
// that read up to the end of the contents will return io.EOF instead of waiting
// for more data. Close is idempotent.
func (fp *FilePipe) Close() error {
fp.mu.Lock()
fp.closing = true
fp.cond.Broadcast()
fp.mu.Unlock()
return nil
}
type filePipeReader struct {
fp *FilePipe
pos int64
}
func (fpr *filePipeReader) Read(buf []byte) (int, error) {
if fpr.fp.f == nil {
return 0, errors.New("filepipe already had its resources released")
}
for {
n, err := fpr.fp.f.ReadAt(buf, fpr.pos)
fpr.pos += int64(n)
if err == io.EOF {
err = nil
}
if err != nil || n > 0 {
return n, err
}
fpr.fp.mu.Lock()
for fpr.pos >= fpr.fp.size && !fpr.fp.closing {
fpr.fp.cond.Wait()
}
eof := fpr.pos >= fpr.fp.size && fpr.fp.closing
fpr.fp.mu.Unlock()
if eof {
return 0, io.EOF
}
}
}

View File

@@ -0,0 +1,141 @@
package main
import (
"bytes"
"io"
"io/ioutil"
"sync"
"testing"
)
// infiniteReader generates an infinite and deterministic stream of bytes
type infiniteReader int
func (ir *infiniteReader) Read(buf []byte) (int, error) {
for i := range buf {
// We want the cycle to be long to detect wrong read offsets more surely.
buf[i] = byte(*ir ^ (*ir >> 8))
*ir++
}
return len(buf), nil
}
func testReader() io.Reader {
const N = 100 * 1024 // more than 2*32k, so that io.Copy will do 3 reads from it
var ir infiniteReader
return io.LimitReader(&ir, N)
}
func expectEqual(t *testing.T, got, want []byte) {
if bytes.Equal(got, want) {
return
}
size := len(want)
if len(got) < size {
size = len(got)
}
for i := 0; i < size; i++ {
if want[i] != got[i] {
t.Errorf("value read differs from expected on byte %d: got=%d, want=%d", i, got[i], want[i])
return
}
}
if len(got) != len(want) {
t.Errorf("value read is %d bytes long, where %d was expected", len(got), len(want))
}
}
func TestFilePipeSimple(t *testing.T) {
fp, err := NewFilePipe()
if err != nil {
t.Fatalf("Failed to create a filepipe: %v", err)
}
fpr := fp.Reader()
_, err = io.Copy(fp, testReader())
if err != nil {
t.Errorf("Failed to write to a filepipe: %v", err)
}
err = fp.Close()
if err != nil {
t.Errorf("Failed to close a filepipe: %v", err)
}
got, err := ioutil.ReadAll(fpr)
if err != nil {
t.Fatalf("Failed to read from a filepipe reader: %v", err)
}
want, err := ioutil.ReadAll(testReader())
if err != nil {
t.Fatalf("error reading from a testReader: %v", err)
}
expectEqual(t, got, want)
}
func TestFilePipeConcurrent(t *testing.T) {
want, err := ioutil.ReadAll(testReader())
if err != nil {
t.Fatalf("error reading from a testReader: %v", err)
}
fp, err := NewFilePipe()
if err != nil {
t.Fatalf("Failed to create a filepipe: %v", err)
}
var wg sync.WaitGroup
const P = 10
for i := 0; i < P; i++ {
wg.Add(1)
fpr := fp.Reader()
go func(fpr io.Reader) {
buf, err := ioutil.ReadAll(fpr)
if err != nil {
t.Fatalf("Failed to read from a filepipe reader: %v", err)
}
expectEqual(t, buf, want)
wg.Done()
}(fpr)
}
_, err = io.Copy(fp, testReader())
if err != nil {
t.Errorf("Failed to write to a filepipe: %v", err)
}
err = fp.Close()
if err != nil {
t.Errorf("Failed to close a filepipe: %v", err)
}
wg.Wait()
}
func TestFilePipeRelease(t *testing.T) {
fp, err := NewFilePipe()
if err != nil {
t.Fatalf("error creating a filepipe: %v", err)
}
fpr := fp.Reader()
err = fp.Release()
if err != nil {
t.Fatalf("error releasing a filepipe: %v", err)
}
var buf [10]byte
_, err = fpr.Read(buf[:])
if err == nil {
t.Errorf("no error when reading from a destroyed filepipe")
}
err = fp.Release()
if err != nil {
t.Fatalf("error releasing a filepipe for the second time: %v", err)
}
}
func TestFilePipeClose(t *testing.T) {
fp, err := NewFilePipe()
if err != nil {
t.Fatalf("error creating a filepipe: %v", err)
}
err = fp.Close()
if err != nil {
t.Fatalf("error closing a filepipe: %v", err)
}
_, err = fp.Write([]byte("foo"))
if err == nil {
t.Errorf("no error when writing to a closed filepipe")
}
}

View File

@@ -0,0 +1,17 @@
// This file contains google3-specific code to make the third party tests work
// with blaze.
package main
import (
"fmt"
"os"
"path/filepath"
)
func init() {
dir := filepath.Join(os.Getenv("TEST_SRCDIR"), "google3/third_party/golang/parunner")
if err := os.Chdir(dir); err != nil {
panic(fmt.Sprintf("os.Chdir(%q): %v", dir, err))
}
}

View File

@@ -0,0 +1,91 @@
package main
import (
"errors"
"os"
"os/exec"
"sync"
"time"
)
type Instance struct {
ID int
TotalInstances int
Cmd *exec.Cmd
RequestChan chan *request
ResponseChan chan *response
// The following fields should not be accessed until the Instance is Waited for.
MessagesSent int
MessageBytesSent int
TimeRunning time.Duration
TimeBlocked time.Duration
errOnce sync.Once
err error
waitDone chan bool
commDone chan bool
}
func (instance *Instance) Start() error {
instance.waitDone = make(chan bool)
instance.commDone = make(chan bool)
cmdr, cmdw, err := os.Pipe()
if err != nil {
return err
}
respr, respw, err := os.Pipe()
if err != nil {
return err
}
if err := startInstance(instance.Cmd, respr, cmdw); err != nil {
return err
}
go func() {
if err := instance.communicate(cmdr, respw, instance.RequestChan, instance.ResponseChan); err != nil {
instance.errOnce.Do(func() {
instance.err = err
})
instance.Cmd.Process.Kill()
}
cmdr.Close()
respw.Close()
close(instance.commDone)
}()
go func() {
err := instance.Cmd.Wait()
instance.errOnce.Do(func() {
instance.err = err
})
instance.TimeRunning = instance.Cmd.ProcessState.SystemTime() + instance.Cmd.ProcessState.UserTime()
// We are doing it this late in order to delay error reports from communicate that are
// a result of the pipes closing (broken pipe on write pipe, EOF on read pipe). We
// do want to ignore some of those errors (e.g. broken pipe at the very beginning, which
// indicates that the program didn't use the communication library at all), so currently
// we ignore all of them.
// TODO: Do we want to ignore then also when the program has terminated with no errors?
// Example: program has exited in the middle of sending a message.
respr.Close()
cmdw.Close()
close(instance.waitDone)
}()
return nil
}
func (i *Instance) Wait() error {
<-i.waitDone
<-i.commDone
return i.err
}
var ErrKilled = errors.New("killed by an explicit request")
func (i *Instance) Kill() error {
i.errOnce.Do(func() {
i.err = ErrKilled
})
return i.Cmd.Process.Kill()
}

View File

@@ -0,0 +1,208 @@
package main
import (
"bytes"
"io/ioutil"
"os"
"os/exec"
"reflect"
"strings"
"testing"
"time"
)
func checkedWait(t *testing.T, instance *Instance) error {
ch := make(chan error, 1)
go func() {
ch <- instance.Wait()
}()
err := instance.Wait()
if err1 := <-ch; err1 != err {
t.Errorf("Instance.Wait() gave contradictory return values: %v != %v", err, err1)
}
return err
}
func TestInstanceSuccess(t *testing.T) {
instance := &Instance{ID: 0, TotalInstances: 1, Cmd: exec.Command(testerPath)}
if err := instance.Start(); err != nil {
t.Fatalf("error starting an instance of tester: %v", err)
}
if err := checkedWait(t, instance); err != nil {
t.Fatalf("error running tester with empty stdin: %v", err)
}
}
func TestInstanceFailure(t *testing.T) {
cmd := exec.Command(testerPath)
cmd.Stdin = strings.NewReader("Q 1\n")
instance := &Instance{ID: 0, TotalInstances: 1, Cmd: cmd}
if err := instance.Start(); err != nil {
t.Fatalf("error starting an instance of tester: %v", err)
}
if err := checkedWait(t, instance); err == nil {
t.Fatalf("no error when running tester with stdin Q 1")
}
}
func TestInstanceKill(t *testing.T) {
cmd := exec.Command(testerPath)
if _, err := cmd.StdinPipe(); err != nil {
t.Fatalf("error in Cmd.StdinPipe: %v", err)
}
cmd.Stdout = ioutil.Discard
instance := &Instance{ID: 0, TotalInstances: 1, Cmd: cmd}
if err := instance.Start(); err != nil {
t.Fatalf("error starting an instance of tester: %v", err)
}
waitChan := make(chan error)
go func() {
waitChan <- checkedWait(t, instance)
}()
// The instance shouldn't finish of its own accord
select {
case err := <-waitChan:
t.Fatalf("tester has finished prematurely, err=%v", err)
case <-time.After(100 * time.Millisecond):
}
instance.Kill()
if err := <-waitChan; err != ErrKilled {
t.Errorf("a killed instance has finished with error %v, instead of %v", err, ErrKilled)
}
}
func TestInstanceComm(t *testing.T) {
if _, err := os.Stat(testerPath); err != nil {
t.Fatalf("can't find tester binary: %v", err)
}
type testcase struct {
name string
input string
expectedOutput string
expectedRequests []*request // expectedRequests[].time is a lower bound on the actual time
responses []*response
}
singleCase := func(tc testcase) {
cmd := exec.Command(testerPath)
cmd.Stdin = strings.NewReader(tc.input)
var stdout bytes.Buffer
cmd.Stdout = &stdout
instance := &Instance{
ID: 5,
TotalInstances: 20,
Cmd: cmd,
RequestChan: make(chan *request, 1),
ResponseChan: make(chan *response, 1),
}
if err := instance.Start(); err != nil {
t.Errorf("test %s: error starting an instance of tester: %v", tc.name, err)
return
}
quit := make(chan bool)
lastReqTime := make(chan time.Duration, 1)
go func() {
var prevTime time.Duration
var i int
for req := range instance.RequestChan {
if req.time < prevTime {
t.Errorf("test %s: request %+v is earlier than %v, the time of the previous request", tc.name, req, prevTime)
}
if i < len(tc.expectedRequests) {
if req.time < tc.expectedRequests[i].time {
t.Errorf("test %s: request %+v has time %v, expected at least %v", tc.name, req, req.time, tc.expectedRequests[i].time)
}
realTime := req.time
req.time = tc.expectedRequests[i].time
if got, want := req, tc.expectedRequests[i]; !reflect.DeepEqual(got, want) {
got.time = realTime
t.Errorf("test %s: got request %+v, expected %+v", tc.name, got, want)
}
} else {
t.Errorf("test %s: got request number %d, expected %d total", tc.name, i, len(tc.expectedRequests))
}
i++
}
if i < len(tc.expectedRequests) {
t.Errorf("test %s: got only %d requests, expected %d", tc.name, i, len(tc.expectedRequests))
}
lastReqTime <- prevTime
<-quit
}()
go func() {
defer func() { <-quit }()
for i, resp := range tc.responses {
select {
case instance.ResponseChan <- resp:
case <-quit:
t.Errorf("test %s: instance was done before receiving response number %d", tc.name, i)
return
}
}
<-quit
}()
defer func() {
quit <- true
quit <- true
quit <- true
}()
if err := checkedWait(t, instance); err != nil {
t.Fatalf("test %s: error running an instance of tester: %v", tc.name, err)
close(instance.RequestChan)
return
}
close(instance.RequestChan)
if got, want := strings.Replace(stdout.String(), "\r\n", "\n", -1), tc.expectedOutput; got != want {
t.Errorf("test %s: wrong output; got=%q, want=%q", tc.name, got, want)
}
if rt := <-lastReqTime; instance.TimeRunning < rt {
t.Errorf("test %s: instance's last request happened at %v, but instance used only %v CPU time total", tc.name, rt, instance.TimeRunning)
}
}
testcases := []testcase{
{"header", "", "5 20\n", []*request{}, []*response{}},
{"send after cpuburn", "C\nScfoobar\n", "5 20\n", []*request{&request{requestType: requestSend, destination: 2, message: []byte("foobar")}}, []*response{}},
{"send", "Scfoobar\n", "5 20\n", []*request{&request{requestType: requestSend, destination: 2, message: []byte("foobar")}}, []*response{}},
{"recv", "Rd\n", "5 20\n3 6 foobaz\n", []*request{&request{requestType: requestRecv, source: 3}}, []*response{&response{&Message{Source: 3, Target: 5, Message: []byte("foobaz")}}}},
{"recvany", "R*\n", "5 20\n3 6 foobaz\n", []*request{&request{requestType: requestRecvAny}}, []*response{&response{&Message{Source: 3, Target: 5, Message: []byte("foobaz")}}}},
{"blockingTime", "R*\nScblah\n", "5 20\n3 6 foobaz\n", []*request{
&request{requestType: requestRecvAny},
&request{requestType: requestSend, time: time.Duration(1234), destination: 2, message: []byte("blah")},
}, []*response{&response{&Message{Source: 3, Target: 5, SendTime: time.Duration(1234), Message: []byte("foobaz")}}}},
}
for _, tc := range testcases {
singleCase(tc)
}
}
// Stop receiving in the middle of a message
func TestInstanceBrokenPipe(t *testing.T) {
cmd := exec.Command(hangerPath)
instance := &Instance{
ID: 0,
TotalInstances: 2,
Cmd: cmd,
RequestChan: make(chan *request, 1),
ResponseChan: make(chan *response, 1),
}
if err := instance.Start(); err != nil {
t.Fatalf("error starting an instance of hanger: %v", err)
}
go func() {
for _ = range instance.RequestChan {
}
}()
defer close(instance.RequestChan)
instance.ResponseChan <- &response{&Message{
Source: 1,
Target: 0,
SendTime: time.Duration(0),
Message: []byte("abcdefghijlkmnopqrstuvwxyz"), // this message will take >20 bytes on the wire
}}
if err := checkedWait(t, instance); err != nil {
t.Fatalf("error running an instance of hanger: %v", err)
}
}

View File

@@ -0,0 +1,13 @@
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package main
import (
"os"
"os/exec"
)
func startInstance(cmd *exec.Cmd, r *os.File, w *os.File) error {
cmd.ExtraFiles = []*os.File{r, w}
return cmd.Start()
}

View File

@@ -0,0 +1,28 @@
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
)
func startInstance(cmd *exec.Cmd, r *os.File, w *os.File) error {
var rHandle syscall.Handle
var wHandle syscall.Handle
p, _ := syscall.GetCurrentProcess()
if err := syscall.DuplicateHandle(p, syscall.Handle(r.Fd()), p, &rHandle, 0, true, syscall.DUPLICATE_SAME_ACCESS); err != nil {
return err
}
defer syscall.CloseHandle(rHandle)
if err := syscall.DuplicateHandle(p, syscall.Handle(w.Fd()), p, &wHandle, 0, true, syscall.DUPLICATE_SAME_ACCESS); err != nil {
return err
}
defer syscall.CloseHandle(wHandle)
if cmd.Env == nil {
cmd.Env = os.Environ()
}
cmd.Env = append(cmd.Env, fmt.Sprintf("ZSHANDLE_IN=%d", rHandle), fmt.Sprintf("ZSHANDLE_OUT=%d", wHandle))
return cmd.Start()
}

View File

@@ -0,0 +1,103 @@
package main
import (
"fmt"
"io"
"os/exec"
"sync"
)
// InstanceError is an error with an associated instance ID
type InstanceError struct {
ID int
Err error
}
func (ie InstanceError) Error() string {
return fmt.Sprintf("Error of instance %d: %v", ie.ID, ie.Err)
}
// RunInstances starts each command from cmds in an Instance and
// waits either for all of them to finish successfully or for
// the first error. In the latter case, all the rest of
// the instances are killed. All the instances are then returned
// in the slice. RunInstances additionally guarantees the following:
// * The instance slice is valid even if the error is non-nil
// * All the commands have been started before RunInstances returns
// * All the instanced have been waited on before RunInstances returns
// * If the error encountered is associated with an instance,
// an instance of InstanceError is returned. That instance contains
// the instance ID of the instance that caused the error.
func RunInstances(cmds []*exec.Cmd, commLog io.Writer) ([]*Instance, error) {
var wg sync.WaitGroup
defer wg.Wait()
results := make(chan error, 1)
is := make([]*Instance, len(cmds))
for i, cmd := range cmds {
is[i] = &Instance{
ID: i,
TotalInstances: len(cmds),
Cmd: cmd,
RequestChan: make(chan *request, 1),
ResponseChan: make(chan *response, 1),
}
if err := is[i].Start(); err != nil {
select {
case results <- InstanceError{i, err}:
default:
}
close(is[i].RequestChan)
continue
}
defer is[i].Kill()
wg.Add(1)
go func(i int, instance *Instance) {
err := instance.Wait()
if err != nil {
select {
case results <- InstanceError{i, err}:
default:
}
}
// The instance leaves the communication channels open. We close the RequestChan
// to signal the message router that this instance has finished. In case of an error,
// we need to do this after possibly storing the error, so that message router's error
// (e.g. ErrDeadlock due to the last nonblocked instance exising) doesn't override ours.
close(instance.RequestChan)
wg.Done()
}(i, is[i])
}
wg.Add(1)
go func() {
requestChans := make([]<-chan *request, len(is))
for i := range requestChans {
requestChans[i] = is[i].RequestChan
}
responseChans := make([]chan<- *response, len(is))
for i := range responseChans {
responseChans[i] = is[i].ResponseChan
}
defer func() {
for _, ch := range responseChans {
close(ch)
}
}()
err := RouteMessages(requestChans, responseChans, commLog)
if err != nil {
select {
case results <- err:
default:
}
}
wg.Done()
}()
go func() {
wg.Wait()
select {
case results <- nil:
default:
}
}()
return is, <-results
}

View File

@@ -0,0 +1,88 @@
package main
import (
"bytes"
"io/ioutil"
"os"
"os/exec"
"strings"
"testing"
)
func TestInstances(t *testing.T) {
if _, err := os.Stat(testerPath); err != nil {
t.Fatalf("can't find tester binary: %v", err)
}
type testcase struct {
name string
inputs []string
expectedOutputs []string // only used if len(expectedFails) == 0
expectedFails []int // if empty, we expect success; otherwise we expect failure from one of the numerated instances
expectedDeadlock bool
}
testcases := []*testcase{
{"no comm", []string{"", "", "", ""}, []string{"0 4\n", "1 4\n", "2 4\n", "3 4\n"}, nil, false},
{"fail", []string{"", "Q 1", "Q 2"}, nil, []int{1, 2}, false},
{"send with no recv", []string{"", "Safoo\n"}, []string{"0 2\n", "1 2\n"}, nil, false},
{"send with recv", []string{"Rb\n", "Safoo\n"}, []string{"0 2\n1 3 foo\n", "1 2\n"}, nil, false},
{"send with recvany", []string{"R*\n", "Safoo\n"}, []string{"0 2\n1 3 foo\n", "1 2\n"}, nil, false},
{"deadlock", []string{"Scfoo\nR*\n", "R*\n", ""}, nil, nil, true},
{"fail and hang", []string{"H\n", "Q 1\n"}, nil, []int{1}, false},
{"fail and hanging recv", []string{"R*\n", "Q 1\n"}, nil, []int{1}, false},
}
for _, tc := range testcases {
outputs := make([]bytes.Buffer, len(tc.inputs))
cmds := make([]*exec.Cmd, len(tc.inputs))
for i, input := range tc.inputs {
cmds[i] = exec.Command(testerPath)
cmds[i].Stdin = strings.NewReader(input)
cmds[i].Stdout = &outputs[i]
}
_, err := RunInstances(cmds, ioutil.Discard)
if _, ok := err.(ErrRemainingMessages); ok {
err = nil
}
if err != nil {
switch err := err.(type) {
case InstanceError:
ok := false
for _, i := range tc.expectedFails {
if err.ID == i {
ok = true
}
}
if !ok {
t.Errorf("test %s: unexpected error from RunInstances: %v", tc.name, err)
}
case ErrDeadlock:
if !tc.expectedDeadlock {
t.Errorf("test %s: unexpected deadlock", tc.name)
}
default:
t.Errorf("test %s: unexpected error from RunInstances: %v", tc.name, err)
}
continue
}
if len(tc.expectedFails) != 0 || tc.expectedDeadlock {
t.Errorf("test %s: unexpected success of RunInstances", tc.name)
continue
}
for i, want := range tc.expectedOutputs {
got := strings.Replace(outputs[i].String(), "\r\n", "\n", -1)
if got != want {
t.Errorf("test %s: wrong output from instance %d: got=%q, want=%q", tc.name, i, got, want)
}
}
}
}
func TestInstancesStartError(t *testing.T) {
cmds := []*exec.Cmd{exec.Command("/does/not/exist")}
_, err := RunInstances(cmds, ioutil.Discard)
if err == nil {
t.Errorf("expected an error when trying to run a nonexistent binary")
}
}
// TODO: check what happens when we send/recv message to/from an instance that doesn't exist
// TODO: check what happens when an instance claims that its CPU time goes backward

View File

@@ -0,0 +1,221 @@
package main
import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"text/tabwriter"
"time"
)
const MaxInstances = 100
var nInstances = flag.Int("n", 1, fmt.Sprintf("Number of instances; must be from the [1,%d] range", MaxInstances))
var stdoutHandling = flag.String("stdout", "contest", "Stdout handling: contest, all, tagged, files")
var stderrHandling = flag.String("stderr", "all", "Stderr handling: all, tagged, files")
var filesPrefix = flag.String("prefix", "", "Filename prefix for files generated by -stdout=files and -stderr=files")
var warnRemaining = flag.Bool("warn_unreceived", true, "Warn about messages that remain unreceived after instance's termination")
var stats = flag.Bool("print_stats", false, "Print per-instance statistics")
var traceCommunications = flag.Bool("trace_comm", false, "Print out a trace of all messages exchanged")
var binaryPath string
func writeFile(streamType string, i int, r io.Reader) error {
binaryDir, binaryFile := filepath.Split(binaryPath)
if idx := strings.LastIndex(binaryFile, "."); idx != -1 {
binaryFile = binaryFile[:idx]
}
basename := filepath.Join(binaryDir, binaryFile)
if *filesPrefix != "" {
basename = *filesPrefix
}
filename := fmt.Sprintf("%s.%s.%d", basename, streamType, i)
f, err := os.Create(filename)
if err != nil {
return err
}
_, err = io.Copy(f, r)
if err1 := f.Close(); err == nil {
err = err1
}
return err
}
func Usage() {
fmt.Fprintf(os.Stderr, "Usage: %s [flags] binary_to_run\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, `Output handling modes:
contest: Fail if more than one instance write any output. Redirect the output to the standard output of this program.
all: Redirect all the instances' outputs to the corresponding output of this program.
tagged: Redirect all the instances' outputs to the corresponding output of this program, while prefixing each line with instance number.
files: Store output of each instance in a separate file.
`)
}
func main() {
log.SetFlags(log.Lmicroseconds | log.Lshortfile)
flag.Usage = Usage
flag.Parse()
if flag.NArg() != 1 {
fmt.Fprintf(os.Stderr, "Specify the binary name\n")
flag.Usage()
os.Exit(1)
}
var err error
binaryPath, err = filepath.Abs(flag.Arg(0))
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot find absolute path of the binary: %v\n", err)
os.Exit(1)
}
if *nInstances < 1 || *nInstances > MaxInstances {
fmt.Fprintf(os.Stderr, "Number of instances should be from [1,%d], but %d was given\n", MaxInstances, *nInstances)
flag.Usage()
os.Exit(1)
}
var writeStdout func(int, io.Reader) error
contestStdout := &ContestStdout{Output: os.Stdout}
switch *stdoutHandling {
case "contest":
// This is handled specially (without a pipe) below.
case "all":
case "tagged":
writeStdout = func(i int, r io.Reader) error { return TagStream(fmt.Sprintf("STDOUT %d: ", i), os.Stdout, r) }
case "files":
writeStdout = func(i int, r io.Reader) error { return writeFile("stdout", i, r) }
default:
fmt.Fprintf(os.Stderr, "Invalid stdout handling mode: %s", *stdoutHandling)
flag.Usage()
os.Exit(1)
}
var writeStderr func(int, io.Reader) error
switch *stderrHandling {
case "all":
case "tagged":
writeStderr = func(i int, r io.Reader) error { return TagStream(fmt.Sprintf("STDERR %d: ", i), os.Stderr, r) }
case "files":
writeStdout = func(i int, r io.Reader) error { return writeFile("stderr", i, r) }
default:
fmt.Fprintf(os.Stderr, "Inalid stderr handling mode: %s", *stdoutHandling)
flag.Usage()
os.Exit(1)
}
stdinPipe, err := NewFilePipe()
if err != nil {
log.Fatal(err)
}
defer stdinPipe.Release()
go func() {
_, err := io.Copy(stdinPipe, os.Stdin)
if err != nil {
log.Fatal(err)
}
err = stdinPipe.Close()
if err != nil {
log.Fatal(err)
}
}()
progs := make([]*exec.Cmd, *nInstances)
var wg sync.WaitGroup
closeAfterWait := []io.Closer{}
for i := range progs {
cmd := exec.Command(binaryPath)
w, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
go func() {
// We don't care about errors from the writer (we expect broken pipe if the process has exited
// before reading all of its input), but we do care about errors when reading from the filepipe.
if _, err := io.Copy(WrapWriter(w), stdinPipe.Reader()); err != nil {
if _, ok := err.(WriterError); !ok {
log.Fatal(err)
}
}
w.Close()
}()
makeFromWrite := func(writeProc func(int, io.Reader) error, w io.Writer) io.Writer {
if writeProc == nil {
return w
}
pr, pw := io.Pipe()
closeAfterWait = append(closeAfterWait, pw)
i := i
wg.Add(1)
go func() {
err := writeProc(i, pr)
if err != nil {
// All the errors we can get are not caused by instances' invalid behaviour, but
// by system issues (can't create a file, broken pipe on real stdout/err, etc.)
log.Fatal(err)
}
wg.Done()
}()
return pw
}
if *stdoutHandling == "contest" {
cmd.Stdout = contestStdout.NewWriter(i)
} else {
cmd.Stdout = makeFromWrite(writeStdout, os.Stdout)
}
cmd.Stderr = makeFromWrite(writeStderr, os.Stderr)
progs[i] = cmd
}
commLog := ioutil.Discard
if *traceCommunications {
commLog = os.Stderr
}
instances, err := RunInstances(progs, commLog)
for _, f := range closeAfterWait {
f.Close()
}
wg.Wait()
if er, ok := err.(ErrRemainingMessages); ok {
if *warnRemaining {
m := make(map[int][]int)
for _, p := range er.RemainingMessages {
m[p.To] = append(m[p.To], p.From)
}
fmt.Fprintf(os.Stderr, "Warning: following instances had some messages left after they've terminated:\n")
for dest, srcs := range m {
fmt.Fprintf(os.Stderr, "Instance %d did not receive message from instances: ", dest)
for _, src := range srcs {
fmt.Fprintf(os.Stderr, "%d ", src)
}
fmt.Fprintln(os.Stderr)
}
}
err = nil
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
var maxTime time.Duration
var lastInstance int
for i, instance := range instances {
if instanceTime := instance.TimeRunning + instance.TimeBlocked; instanceTime >= maxTime {
maxTime = instanceTime
lastInstance = i
}
}
fmt.Fprintf(os.Stderr, "Duration: %v (longest running instance: %d)\n", maxTime, lastInstance)
if *stats {
w := tabwriter.NewWriter(os.Stderr, 2, 1, 1, ' ', 0)
io.WriteString(w, "Instance\tTotal time\tCPU time\tTime spent waiting\tSent messages\tSent bytes\n")
for i, instance := range instances {
fmt.Fprintf(w, "%d\t%v\t%v\t%v\t%d\t%d\n", i, instance.TimeRunning+instance.TimeBlocked, instance.TimeRunning, instance.TimeBlocked, instance.MessagesSent, instance.MessageBytesSent)
}
w.Flush()
}
}

View File

@@ -0,0 +1,8 @@
# Description:
# Auto-imported from github.com/robryk/parunner/message
package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # BSD 3-clause
exports_files(["LICENSE"])

View File

@@ -0,0 +1,127 @@
#!/bin/bash
set -u
ZEUS_DIR=$(realpath $(dirname $(which $0)))
CFLAGS="${CFLAGS:-} -O2 -static -I${ZEUS_DIR}"
CXXFLAGS="${CXXFLAGS:-} -O2 -static -std=gnu++0x -I${ZEUS_DIR}"
CXX=${CXX:-g++}
CC=${CC:-gcc}
FPC=${FPC:-fpc}
read -d '' USAGE <<EOF
Uzycie: $0 [--debug] source_file [library_file]
Przyklad:
$0 solution.cpp
Opcja --debug uzywa debugowej wersji biblioteki message. Wersja ta
sprawdza, czy typy odbierane sa takie same jak typy wysylane (czy
przykladowo nie odebrano chara gdy wyslano inta) oraz czy funkcja
Receive jest poprawnie wywolywana.
EOF
if [ "$#" -eq 0 ]; then
echo "$USAGE"
exit 1
fi
MESSAGE=message_internal
DEBUGPAS=
if [ "$1" == "--debug" ]; then
MESSAGE=message_internal_debug
DEBUGPAS=-dmsg_debug
shift
fi
if [ "$#" -gt 2 -o "$#" -lt 1 ]; then
echo "$USAGE"
exit 1
fi
SOURCE="$1"
LIBRARY_SOURCE="${2:-}"
pushd "${ZEUS_DIR}" > /dev/null
test -f zeus_local.o || "${CC}" -c -O2 -g zeus_local.c || exit 2
test -f "${MESSAGE}.o" || "${CC}" -c -O2 -g "${MESSAGE}.c" || exit 2
popd > /dev/null
PASLIB=0
NONPASLIB=0
case "$LIBRARY_SOURCE" in
"")
LIBRARY_OBJ=
;;
*.pas)
LIBRARY_OBJ=
PASLIB=1
;;
*.cpp)
LIBRARY_OBJ="$(echo "${LIBRARY_SOURCE}" | sed 's/\.cpp$//').impl.o"
"${CXX}" -c ${CXXFLAGS} "$LIBRARY_SOURCE" -o "$LIBRARY_OBJ" || exit 2
NONPASLIB=1
;;
*.c)
LIBRARY_OBJ="$(echo "${LIBRARY_SOURCE}" | sed 's/\.c$//').impl.o"
"${CC}" -c ${CFLAGS} "${LIBRARY_SOURCE}" -o "${LIBRARY_OBJ}" || exit 2
NONPASLIB=1
;;
*)
echo "Nieznany jezyk biblioteczki ${LIBRARY_SOURCE}"
exit 1
;;
esac
case "${SOURCE}" in
*.pas)
if [ "${NONPASLIB}" -eq 1 ]; then
echo "Biblioteczka winna byc napisana w tym samym jezyku co program"
exit 1
fi
BASENAME="$(echo "${SOURCE}" | sed 's/\.pas//')"
rm "${ZEUS_DIR}/message.ppu"
"${FPC}" ${DEBUGPAS} -Fu"${ZEUS_DIR}" -o"${BASENAME}.e" "${SOURCE}" || {
if [ "${PASLIB}" -eq 1 ]; then
echo "Upewnij sie, ze dodales wlasciwa dyrektywe uses dla biblioteczki"
fi
exit 2
}
;;
*.cpp)
if [ "${PASLIB}" -eq 1 ]; then
echo "Biblioteczka winna byc napisana w tym samym jezyku co program"
exit 1
fi
BASENAME="$(echo "$SOURCE" | sed 's/\.cpp//')"
"${CXX}" -c ${CXXFLAGS} "${SOURCE}" -o "${BASENAME}.o" || exit 2
if [ "${LIBRARY_OBJ}" == "" ]; then
"${CXX}" -I"${ZEUS_DIR}" "${ZEUS_DIR}/${MESSAGE}.o" "${ZEUS_DIR}/zeus_local.o" "${BASENAME}.o" -o "${BASENAME}.e" || exit 2
else
"${CXX}" -I"${ZEUS_DIR}" "${ZEUS_DIR}/${MESSAGE}.o" "${ZEUS_DIR}/zeus_local.o" "${BASENAME}.o" "${LIBRARY_OBJ}" -o "${BASENAME}.e" || exit 2
fi
;;
*.c)
if [ "${PASLIB}" -eq 1 ]; then
echo "Biblioteczka winna byc napisana w tym samym jezyku co program"
exit 1
fi
BASENAME="$(echo "${SOURCE}" | sed 's/\.c//')"
"${CC}" -c ${CFLAGS} "${SOURCE}" -o "${BASENAME}.o" || exit 2
if [ "$LIBRARY_OBJ" == "" ]; then
"${CXX}" -I"${ZEUS_DIR}" "${ZEUS_DIR}/${MESSAGE}.o" "${ZEUS_DIR}/zeus_local.o" "${BASENAME}.o" -o "${BASENAME}.e" || exit 2
else
"${CXX}" -I"${ZEUS_DIR}" "${ZEUS_DIR}/${MESSAGE}.o" "${ZEUS_DIR}/zeus_local.o" "${BASENAME}.o" "${LIBRARY_OBJ}" -o "${BASENAME}.e" || exit 2
fi
;;
*)
echo "Nieznany jezyk pliku zrodlowego ${SOURCE}"
exit 2
esac
# vim:ts=2:sts=2:sw=2:et:

View File

@@ -0,0 +1,78 @@
// Biblioteka message sluzy do przekazywania wiadomosci pomiedzy instancjami
// programu. Wiadomosc moze skladac sie z dowolnej liczby znakow (char) oraz
// liczb typu int i long long. Maksymalna liczba wiadmosci, jakie moze
// wyslac pojedyncza instancja, to 1000, a ich laczny rozmiar nie moze
// przekraczac 8 MB.
#ifndef MESSAGE_H_
#define MESSAGE_H_
#ifdef __cplusplus
extern "C" {
#endif
// Zwraca liczbe instancji.
// Pascal: function NumberOfNodes:longint;
int NumberOfNodes();
// Zwraca ID biezacej instancji, z przedzialu {0, ..., NumberOfNodes()-1}.
// Pascal: function MyNodeId:longint;
int MyNodeId();
// Kolejkuje `value` do wyslania do instancji `target`.
// Pascal: procedure PutChar(target:longint; value:shortint);
void PutChar(int target, char value);
// Kolejkuje `value` do wyslania do instancji `target`.
// Pascal: procedure PutInt(target:longint; value:longint);
void PutInt(int target, int value);
// Kolejkuje `value` do wyslania do instancji `target`.
// Pascal: procedure PutLL(target:longint; value:int64);
void PutLL(int target, long long value);
// Wysyla zakolejkowana wiadomosc do instancji `target`. Powrot z funkcji
// nastepuje natychmiast, nie czekajac na odbior wiadomosci.
// Pascal: procedure Send(target:longint);
void Send(int target);
// Odbiera wiadomosc od instancji `source` (lub dowolnej, gdy `source` == -1).
// Zwraca numer instancji, od ktorej wiadomosc odebral. Receive wymaga, aby w
// momencie jego wywolania poprzednio odebrana wiadomosc od instancji `source`
// (lub wszystkie poprzednio odebrane wiadomosci, gdy `source` == -1) byla
// w calosci przeczytana.
// Pascal: function Receive(source:longint):longint;
int Receive(int source);
// Po odebraniu wiadomosci jej zawartosc nalezy czytac w takiej kolejnosci,
// w jakiej byla kolejkowana do wyslania. Np. proba odczytania int-a, podczas
// gdy pierwsza zakolejkowana wartosc byl long long, skonczy sie bledem
// wykonania (jesli program byl kompilowany z opcja --debug) lub spowoduje
// niezdefiniowane zachowanie (bez --debug).
// Po odebraniu wiadomosci jej zawartosc nalezy czytac w takiej kolejnosci,
// w jakiej byla kolejkowana do wyslania. Na przyklad, gdy nadawca jako pierwsza
// wartosc zakolejkowal long longa, a odbiorca sprobuje przeczytac chara,
// program zakonczy sie bledem wykonania (jesli byl kompilowany z opcja --debug)
// lub zachowa sie w niezdefiniowany sposob (bez --debug).
// Czyta char z odebranej wiadomosci od instancji `source`. Numer instancji
// musi byc liczba z przedzialu {0, ..., NumberOfNodes()-1}. W szczegolnosci
// nie moze byc rowny -1.
// Pascal: function GetChar(source:longint):shortint;
char GetChar(int source);
// Czyta int z odebranej wiadomosci od instancji `source`.
// Pascal: function GetInt(source:longint):longint;
int GetInt(int source);
// Czyta long long z odebranej wiadomosci od instancji `source`.
// Pascal: function GetLL(source:longint):int64;
long long GetLL(int source);
#ifdef __cplusplus
}
#endif
#endif // MESSAGE_H_

View File

@@ -0,0 +1,170 @@
#include "message.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "zeus.h"
#define ZEUS(s) zeus_##s
#define DEBUG 0
#define MAX_MESSAGE_SIZE (8 * (1<<20))
static void Die(const char* s) {
fputs(s, stderr);
exit(20);
}
int NumberOfNodes() {
return ZEUS(NumberOfNodes)();
}
int MyNodeId() {
return ZEUS(MyNodeId)();
}
static void CheckNodeId(int node) {
if (!DEBUG)
return;
if (node < 0 || node >= NumberOfNodes())
Die("Niepoprawny numer maszyny");
}
typedef struct Buffer {
char* buffer;
int size;
int pos; // for input buffers next byte to be read. for output buffers next byte to be written.
} Buffer;
static int Empty(Buffer* buffer) {
return buffer->pos >= buffer->size;
}
static unsigned char GetRawByte(Buffer* buf) {
if (Empty(buf)) {
Die("Przeczytano za koncem wiadomosci");
}
char r = buf->buffer[buf->pos++];
if (Empty(buf)) {
free(buf->buffer);
buf->buffer = NULL;
buf->pos = 0;
buf->size = 0;
}
return r;
}
static void PutRawByte(Buffer* buffer, unsigned char byte) {
if (buffer->pos >= buffer->size) {
buffer->size = 2*buffer->size;
if (buffer->size < 128)
buffer->size = 128;
buffer->buffer = (char*)realloc(buffer->buffer, buffer->size);
assert(buffer->buffer);
}
buffer->buffer[buffer->pos++] = byte;
}
#define MAX_MACHINES 100
static Buffer incoming_buffers[MAX_MACHINES];
static Buffer outgoing_buffers[MAX_MACHINES];
char recv_buffer[MAX_MESSAGE_SIZE];
int Receive(int source) {
if (source != -1)
CheckNodeId(source);
if (DEBUG && source == -1) {
int i;
for(i=0;i<ZEUS(NumberOfNodes)();i++) {
if (!Empty(&incoming_buffers[i]))
Die("Receive(-1) z nieprzeczytana wiadomoscia");
}
}
ZEUS(MessageInfo) mi = ZEUS(Receive)(source, recv_buffer, sizeof(recv_buffer));
Buffer* buf = &incoming_buffers[mi.sender_id];
if (!Empty(buf))
Die("Receive() odebral wiadomosc od maszyny z nieprzeczytana wiadomoscia");
assert(buf->buffer == NULL);
buf->buffer = (char*)malloc(mi.length);
assert(buf->buffer);
memcpy(buf->buffer, recv_buffer, mi.length);
buf->pos = 0;
buf->size = mi.length;
return mi.sender_id;
}
#define kChar 14
#define kInt 15
#define kLL 16
static void GetTag(int source, int expected) {
if (!DEBUG)
return;
int tag = GetRawByte(&incoming_buffers[source]);
if (tag != expected)
Die("Przeczytano inny typ wartosci niz wyslano");
}
int GetInt(int source) {
CheckNodeId(source);
GetTag(source, kInt);
int result = 0, i;
for(i=0;i<sizeof(int);i++)
result |= (int)(GetRawByte(&incoming_buffers[source])) << (8 * i);
return result;
}
void PutInt(int target, int value) {
CheckNodeId(target);
if (DEBUG)
PutRawByte(&outgoing_buffers[target], kInt);
int i;
for(i=0;i<sizeof(int);i++)
PutRawByte(&outgoing_buffers[target], (0xff & (value >> (8 * i))));
}
long long GetLL(int source) {
CheckNodeId(source);
GetTag(source, kLL);
long long result = 0;
int i;
for(i=0;i<sizeof(long long);i++)
result |= (long long)(GetRawByte(&incoming_buffers[source])) << (8 * i);
return result;
}
void PutLL(int target, long long value) {
CheckNodeId(target);
if (DEBUG)
PutRawByte(&outgoing_buffers[target], kLL);
int i;
for(i=0;i<sizeof(long long);i++)
PutRawByte(&outgoing_buffers[target], (0xff & (value >> (8 * i))));
}
char GetChar(int source) {
CheckNodeId(source);
GetTag(source, kChar);
return GetRawByte(&incoming_buffers[source]);
}
void PutChar(int target, char value) {
CheckNodeId(target);
if (DEBUG)
PutRawByte(&outgoing_buffers[target], kChar);
PutRawByte(&outgoing_buffers[target], value);
}
void Send(int target) {
CheckNodeId(target);
Buffer* buffer = &outgoing_buffers[target];
if (buffer->pos > sizeof(recv_buffer))
Die("Za dluga wiadomosc");
ZEUS(Send)(target, buffer->buffer, buffer->pos);
free(buffer->buffer);
buffer->buffer = NULL;
buffer->pos = buffer->size = 0;
}

View File

@@ -0,0 +1,170 @@
#include "message.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "zeus.h"
#define ZEUS(s) zeus_##s
#define DEBUG 1
#define MAX_MESSAGE_SIZE (8 * (1<<20))
static void Die(const char* s) {
fputs(s, stderr);
exit(20);
}
int NumberOfNodes() {
return ZEUS(NumberOfNodes)();
}
int MyNodeId() {
return ZEUS(MyNodeId)();
}
static void CheckNodeId(int node) {
if (!DEBUG)
return;
if (node < 0 || node >= NumberOfNodes())
Die("Niepoprawny numer maszyny");
}
typedef struct Buffer {
char* buffer;
int size;
int pos; // for input buffers next byte to be read. for output buffers next byte to be written.
} Buffer;
static int Empty(Buffer* buffer) {
return buffer->pos >= buffer->size;
}
static unsigned char GetRawByte(Buffer* buf) {
if (Empty(buf)) {
Die("Przeczytano za koncem wiadomosci");
}
char r = buf->buffer[buf->pos++];
if (Empty(buf)) {
free(buf->buffer);
buf->buffer = NULL;
buf->pos = 0;
buf->size = 0;
}
return r;
}
static void PutRawByte(Buffer* buffer, unsigned char byte) {
if (buffer->pos >= buffer->size) {
buffer->size = 2*buffer->size;
if (buffer->size < 128)
buffer->size = 128;
buffer->buffer = (char*)realloc(buffer->buffer, buffer->size);
assert(buffer->buffer);
}
buffer->buffer[buffer->pos++] = byte;
}
#define MAX_MACHINES 100
static Buffer incoming_buffers[MAX_MACHINES];
static Buffer outgoing_buffers[MAX_MACHINES];
char recv_buffer[MAX_MESSAGE_SIZE];
int Receive(int source) {
if (source != -1)
CheckNodeId(source);
if (DEBUG && source == -1) {
int i;
for(i=0;i<ZEUS(NumberOfNodes)();i++) {
if (!Empty(&incoming_buffers[i]))
Die("Receive(-1) z nieprzeczytana wiadomoscia");
}
}
ZEUS(MessageInfo) mi = ZEUS(Receive)(source, recv_buffer, sizeof(recv_buffer));
Buffer* buf = &incoming_buffers[mi.sender_id];
if (!Empty(buf))
Die("Receive() odebral wiadomosc od maszyny z nieprzeczytana wiadomoscia");
assert(buf->buffer == NULL);
buf->buffer = (char*)malloc(mi.length);
assert(buf->buffer);
memcpy(buf->buffer, recv_buffer, mi.length);
buf->pos = 0;
buf->size = mi.length;
return mi.sender_id;
}
#define kChar 14
#define kInt 15
#define kLL 16
static void GetTag(int source, int expected) {
if (!DEBUG)
return;
int tag = GetRawByte(&incoming_buffers[source]);
if (tag != expected)
Die("Przeczytano inny typ wartosci niz wyslano");
}
int GetInt(int source) {
CheckNodeId(source);
GetTag(source, kInt);
int result = 0, i;
for(i=0;i<sizeof(int);i++)
result |= (int)(GetRawByte(&incoming_buffers[source])) << (8 * i);
return result;
}
void PutInt(int target, int value) {
CheckNodeId(target);
if (DEBUG)
PutRawByte(&outgoing_buffers[target], kInt);
int i;
for(i=0;i<sizeof(int);i++)
PutRawByte(&outgoing_buffers[target], (0xff & (value >> (8 * i))));
}
long long GetLL(int source) {
CheckNodeId(source);
GetTag(source, kLL);
long long result = 0;
int i;
for(i=0;i<sizeof(long long);i++)
result |= (long long)(GetRawByte(&incoming_buffers[source])) << (8 * i);
return result;
}
void PutLL(int target, long long value) {
CheckNodeId(target);
if (DEBUG)
PutRawByte(&outgoing_buffers[target], kLL);
int i;
for(i=0;i<sizeof(long long);i++)
PutRawByte(&outgoing_buffers[target], (0xff & (value >> (8 * i))));
}
char GetChar(int source) {
CheckNodeId(source);
GetTag(source, kChar);
return GetRawByte(&incoming_buffers[source]);
}
void PutChar(int target, char value) {
CheckNodeId(target);
if (DEBUG)
PutRawByte(&outgoing_buffers[target], kChar);
PutRawByte(&outgoing_buffers[target], value);
}
void Send(int target) {
CheckNodeId(target);
Buffer* buffer = &outgoing_buffers[target];
if (buffer->pos > sizeof(recv_buffer))
Die("Za dluga wiadomosc");
ZEUS(Send)(target, buffer->buffer, buffer->pos);
free(buffer->buffer);
buffer->buffer = NULL;
buffer->pos = buffer->size = 0;
}

View File

@@ -0,0 +1,48 @@
From 9a3a25a8aec5dde88a322f0905f3c3a56d3ad1d5 Mon Sep 17 00:00:00 2001
From: Robert Obryk <robryk@google.com>
Date: Tue, 28 Apr 2015 00:04:36 +0200
Subject: [PATCH] Fix a nil dereference when an instance fails to start.
---
instances.go | 2 +-
instances_test.go | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git instances.go instances.go
index ea19ff8..f782a79 100644
--- instances.go
+++ instances.go
@@ -43,11 +43,11 @@ func RunInstances(cmds []*exec.Cmd, commLog io.Writer) ([]*Instance, error) {
ResponseChan: make(chan *response, 1),
}
if err := is[i].Start(); err != nil {
- is[i] = nil
select {
case results <- InstanceError{i, err}:
default:
}
+ close(is[i].RequestChan)
continue
}
defer is[i].Kill()
diff --git instances_test.go instances_test.go
index a00f7ef..5c13709 100644
--- instances_test.go
+++ instances_test.go
@@ -76,5 +76,13 @@ func TestInstances(t *testing.T) {
}
}
+func TestInstancesStartError(t *testing.T) {
+ cmds := []*exec.Cmd{exec.Command("/does/not/exist")}
+ _, err := RunInstances(cmds, ioutil.Discard)
+ if err == nil {
+ t.Errorf("expected an error when trying to run a nonexistent binary")
+ }
+}
+
// TODO: check what happens when we send/recv message to/from an instance that doesn't exist
// TODO: check what happens when an instance claims that its CPU time goes backward
--
2.2.0.rc0.207.ga3a616c

View File

@@ -0,0 +1,25 @@
From 40cf238d611563b9d6af96f0fe950eabb6350874 Mon Sep 17 00:00:00 2001
From: Robert Obryk <robryk@google.com>
Date: Tue, 28 Apr 2015 00:29:37 +0200
Subject: [PATCH] go vet
---
instance_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git instance_test.go instance_test.go
index edd0371..28a378f 100644
--- instance_test.go
+++ instance_test.go
@@ -158,7 +158,7 @@ func TestInstanceComm(t *testing.T) {
t.Errorf("test %s: wrong output; got=%q, want=%q", tc.name, got, want)
}
if rt := <-lastReqTime; instance.TimeRunning < rt {
- t.Errorf("test %s: instance's last request happened at %v, but instance used only %v CPU time total", rt, instance.TimeRunning)
+ t.Errorf("test %s: instance's last request happened at %v, but instance used only %v CPU time total", tc.name, rt, instance.TimeRunning)
}
}
testcases := []testcase{
--
2.2.0.rc0.207.ga3a616c

View File

@@ -0,0 +1,54 @@
From 5b52c40f67b2ff06cd893df278e2d3c3e8751797 Mon Sep 17 00:00:00 2001
From: Robert Obryk <robryk@google.com>
Date: Wed, 6 May 2015 19:57:27 +0200
Subject: [PATCH] Use cwd instead of $PATH when looking up the binary.
This causes parunner blah to actually run ./blah instead of erroring
out.
---
main.go | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git main.go main.go
index 74419d7..3fb6b2b 100644
--- main.go
+++ main.go
@@ -63,18 +63,25 @@ func main() {
log.SetFlags(log.Lmicroseconds | log.Lshortfile)
flag.Usage = Usage
flag.Parse()
+
if flag.NArg() != 1 {
fmt.Fprintf(os.Stderr, "Nie podałeś programu do uruchomienia\n")
flag.Usage()
os.Exit(1)
}
- binaryPath = flag.Arg(0)
+ var err error
+ binaryPath, err = filepath.Abs(flag.Arg(0))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot find absolute path of the binary: %v\n", err)
+ os.Exit(1)
+ }
if *nInstances < 1 || *nInstances > MaxInstances {
fmt.Fprintf(os.Stderr, "Liczba instancji powinna być z zakresu [1,%d], a podałeś %d\n", MaxInstances, *nInstances)
flag.Usage()
os.Exit(1)
}
+
var writeStdout func(int, io.Reader) error
contestStdout := &ContestStdout{Output: os.Stdout}
switch *stdoutHandling {
@@ -122,7 +129,7 @@ func main() {
var wg sync.WaitGroup
closeAfterWait := []io.Closer{}
for i := range progs {
- cmd := exec.Command(flag.Arg(0))
+ cmd := exec.Command(binaryPath)
w, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
--
2.2.0.rc0.207.ga3a616c

View File

@@ -0,0 +1,52 @@
From 045611330af23d7a9c1e510a8e69f6fa52f6290b Mon Sep 17 00:00:00 2001
From: Robert Obryk <robryk@google.com>
Date: Wed, 6 May 2015 20:03:05 +0200
Subject: [PATCH] Custom error types for limits exceeded
---
comm.go | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git comm.go comm.go
index c153cee..785bbc2 100644
--- comm.go
+++ comm.go
@@ -49,8 +49,19 @@ type Message struct {
Message []byte
}
-var ErrMessageCount = fmt.Errorf("przekroczony limit (%d) liczby wysłanych wiadomości", MessageCountLimit)
-var ErrMessageSize = fmt.Errorf("przekroczony limit (%d bajtów) sumarycznego rozmiaru wysłanych wiadomości", MessageSizeLimit)
+type ErrMessageCount struct {
+}
+
+func (err ErrMessageCount) Error() string {
+ return fmt.Sprintf("przekroczony limit (%d) liczby wysłanych wiadomości", MessageCountLimit)
+}
+
+type ErrMessageSize struct {
+}
+
+func (err ErrMessageSize) Error() string {
+ return fmt.Sprintf("przekroczony limit (%d bajtów) sumarycznego rozmiaru wysłanych wiadomości", MessageSizeLimit)
+}
func writeMessage(w io.Writer, message *Message) error {
rr := recvResponse{
@@ -175,11 +186,11 @@ func (i *Instance) communicate(r io.Reader, w io.Writer, reqCh chan<- *request,
if req.requestType == requestSend {
i.MessagesSent++
if i.MessagesSent > MessageCountLimit {
- return ErrMessageCount
+ return ErrMessageCount{}
}
i.MessageBytesSent += len(req.message)
if i.MessageBytesSent > MessageSizeLimit {
- return ErrMessageSize
+ return ErrMessageSize{}
}
}
currentTime := req.time
--
2.2.0.rc0.207.ga3a616c

Some files were not shown because too many files have changed in this diff Show More