blob: d323fbbd4bfbec709148d28bad69a3c117c84901 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.mrql;
import org.apache.mrql.gen.*;
import java.io.*;
import java.util.*;
/** Evaluation of MRQL algebra expressions in memory */
final public class MapReduceAlgebra {
/** eager concat-map (not used) */
private static Bag cmap_eager ( final Function f, final Bag s ) {
Bag res = new Bag();
for ( MRData e: s )
res.addAll((Bag)f.eval(e));
return res;
}
/** lazy concat-map (stream-based)
* @param f a function from a to {b}
* @param s the input of type {a}
* @return a value of type {b}
*/
public static Bag cmap ( final Function f, final Bag s ) {
final Iterator<MRData> si = s.iterator();
return new Bag(new BagIterator() {
Iterator<MRData> data = null;
boolean more = false;
public boolean hasNext () {
if (data == null) {
while (!more && si.hasNext()) {
data = ((Bag)f.eval(si.next())).iterator();
more = data.hasNext();
}
} else {
if (more) {
more = data.hasNext();
if (more)
return true;
};
while (!more && si.hasNext()) {
data = ((Bag)f.eval(si.next())).iterator();
more = data.hasNext();
}
};
return more;
}
public MRData next () {
return data.next();
}
});
}
/** lazy map
* @param f a function from a to b
* @param s the input of type {a}
* @return a value of type {b}
*/
public static Bag map ( final Function f, final Bag s ) {
final Iterator<MRData> si = s.iterator();
return new Bag(new BagIterator() {
public boolean hasNext () { return si.hasNext(); }
public MRData next () { return f.eval(si.next()); }
});
}
/** lazy filter combined with a map
* @param p a function from a to boolean
* @param f a function from a to b
* @param s the input of type {a}
* @return a value of type {b}
*/
public static Bag filter ( final Function p, final Function f, final Bag s ) {
final Iterator<MRData> si = s.iterator();
return new Bag(new BagIterator() {
MRData data = null;
public boolean hasNext () {
while (si.hasNext()) {
data = si.next();
if (((MR_bool)p.eval(data)).get())
return true;
};
return false;
}
public MRData next () { return f.eval(data); }
});
}
/** general reduction using an accumulator function and a zero element
* @param acc a function from (a,b) to b
* @param zero a value of type b
* @param s the input of type {a}
* @return a value of type b
*/
public static MRData fold ( final Function acc, final MRData zero, final Bag s ) {
MRData result = zero;
for ( MRData value: s )
result = acc.eval(new Tuple(value,result));
return result;
}
/** strict group-by
* @param s the input of type {(a,b)}
* @return a value of type {(a,{b})}
*/
public static Bag groupBy ( Bag s ) {
Bag res = new Bag();
s.sort();
MRData last = null;
Bag group = new Bag();
for ( MRData e: s) {
final Tuple p = (Tuple)e;
if (last != null && p.first().equals(last))
group.add(p.second());
else {
if (last != null) {
group.trim();
res.add(new Tuple(last,group));
};
last = p.first();
group = new Bag();
group.add(p.second());
}
};
if (last != null) {
group.trim();
res.add(new Tuple(last,group));
};
//res.trim();
return res;
}
/** lazy group-by (not used) */
private static Bag groupBy_lazy ( Bag s ) {
s.sort();
final Iterator<MRData> it = s.iterator();
return new Bag(new BagIterator() {
MRData last = null;
MRData data = null;
Bag group = new Bag();
public boolean hasNext () {
while (it.hasNext()) {
final Tuple p = (Tuple)it.next();
if (last != null && p.first().equals(last))
group.add(p.second());
else if (last != null) {
group.trim();
data = new Tuple(last,group);
last = p.first();
group = new Bag();
group.add(p.second());
return true;
} else {
last = p.first();
group = new Bag();
group.add(p.second());
}
};
if (last != null) {
group.trim();
data = new Tuple(last,group);
last = null;
return true;
};
return false;
}
public MRData next () {
return data;
}
});
}
/** the MapReduce operation
* @param m a map function from a to {(k,b)}
* @param r a reduce function from (k,{b}) to {c}
* @param s the input of type {a}
* @return a value of type {c}
*/
public static Bag mapReduce ( final Function m, final Function r, final Bag s ) {
return cmap(r,groupBy(cmap(m,s)));
}
/** Not used: use mapReduce2 instead */
private static Bag join ( final Function kx, final Function ky, final Function f,
final Bag X, final Bag Y ) {
return cmap(new Function() {
public Bag eval ( final MRData e ) {
final Tuple p = (Tuple)e;
return (Bag)f.eval(new Tuple(p.second(),
cmap(new Function() {
public Bag eval ( final MRData y ) {
return (ky.eval(y).equals(p.first()))
? new Bag(y)
: new Bag();
} }, Y))); }
},
groupBy(cmap(new Function() {
public Bag eval ( final MRData x ) {
return new Bag(new Tuple(kx.eval(x),x));
} }, X)));
}
/** A hash-based equi-join
* @param kx left key function from a to k
* @param ky right key function from b to k
* @param f reducer from (a,b) to c
* @param X left input of type {a}
* @param Y right input of type {b}
* @return a value of type {c}
*/
public static Bag hash_join ( final Function kx, final Function ky, final Function f,
final Bag X, final Bag Y ) {
Hashtable<MRData,Bag> hashTable = new Hashtable<MRData,Bag>(1000);
for ( MRData x: X ) {
MRData key = kx.eval(x);
Bag old = hashTable.get(key);
if (old == null)
hashTable.put(key,new Bag(x));
else old.add(x);
};
Bag res = new Bag();
for ( MRData y: Y ) {
MRData key = ky.eval(y);
Bag match = hashTable.get(key);
if (match != null)
for ( MRData x: match )
res.add(f.eval(new Tuple(x,y)));
};
return res;
}
/** A cross-product
* @param mx left map function from a to {a'}
* @param my right key function from b to {b'}
* @param r reducer from (a',b') to {c}
* @param X left input of type {a}
* @param Y right input of type {b}
* @return a value of type {c}
*/
public static Bag crossProduct ( final Function mx, final Function my, final Function r,
final Bag X, final Bag Y ) {
Bag a = new Bag();
for ( MRData y: Y )
for ( MRData v: (Bag)my.eval(y) )
a.add(v);
Bag b = new Bag();
for ( MRData x: X )
for ( MRData xx: (Bag)mx.eval(x) )
for ( MRData y: a )
for ( MRData v: (Bag)r.eval(new Tuple(xx,y)) )
b.add(v);
return b;
}
/** A map-reduce operation with two mappers (a join)
* @param mx left map function from a to {(k,a')}
* @param my right key function from b to {(k,b')}
* @param r reducer from ({a'},{b'}) to {c}
* @param X left input of type {a}
* @param Y right input of type {b}
* @return a value of type {c}
*/
public static Bag mapReduce2 ( final Function mx, // left mapper
final Function my, // right mapper
final Function r, // reducer
final Bag X, final Bag Y ) {
final Bag left = cmap(new Function() {
public Bag eval ( final MRData x ) {
return cmap(new Function() {
public Bag eval ( final MRData e ) {
final Tuple p = (Tuple)e;
return new Bag(new Tuple(p.first(),
new Tuple(new MR_byte(1),p.second())));
} }, (Bag)mx.eval(x));
} }, X);
final Bag right = cmap(new Function() {
public Bag eval ( final MRData y ) {
return cmap(new Function() {
public Bag eval ( final MRData e ) {
final Tuple p = (Tuple)e;
return new Bag(new Tuple(p.first(),
new Tuple(new MR_byte(2),p.second())));
} }, (Bag)my.eval(y));
} }, Y);
final Iterator<MRData> li = left.iterator();
final Iterator<MRData> ri = right.iterator();
final Bag mix = new Bag(new BagIterator () {
MRData data;
public boolean hasNext () {
if (li.hasNext()) {
data = li.next();
return true;
} else if (ri.hasNext()) {
data = ri.next();
return true;
} else return false;
}
public MRData next () {
return data;
}
});
return cmap(new Function() {
public Bag eval ( final MRData e ) {
final Tuple p = (Tuple)e;
final Bag xs = cmap(new Function() {
public Bag eval ( final MRData e ) {
final Tuple q = (Tuple)e;
return (((MR_byte)q.first()).get() == 1)
? new Bag(q.second())
: new Bag();
} }, (Bag)p.second());
final Bag ys = cmap(new Function() {
public Bag eval ( final MRData e ) {
final Tuple q = (Tuple)e;
return (((MR_byte)q.first()).get() == 2)
? new Bag(q.second())
: new Bag();
} }, (Bag)p.second());
xs.materialize();
ys.materialize();
return (Bag)r.eval(new Tuple(xs,ys));
} }, groupBy(mix));
}
/** The fragment-replicate join (map-side join)
* @param kx left key function from a to k
* @param ky right key function from b to k
* @param r reducer from (a,{b}) to {c}
* @param X left input of type {a}
* @param Y right input of type {b}
* @return a value of type {c}
*/
public static Bag mapJoin ( final Function kx, final Function ky, final Function r,
final Bag X, final Bag Y ) {
X.materialize();
Y.materialize();
return cmap(new Function() {
public Bag eval ( final MRData e ) {
final Tuple p = (Tuple)e;
return cmap(new Function() {
public Bag eval ( final MRData x ) {
return (kx.eval(x).equals(p.first()))
? (Bag)r.eval(new Tuple(x,p.second()))
: new Bag();
} }, X); }
},
groupBy(cmap(new Function() {
public Bag eval ( final MRData y ) {
return new Bag(new Tuple(ky.eval(y),y));
} }, Y)));
}
/** An equi-join combined with a group-by (see GroupByJoinPlan)
* @param kx left key function from a to k
* @param ky right key function from b to k
* @param gx group-by key function from a to k1
* @param gy group-by key function from b to k2
* @param acc accumulator from (c,(a,b)) to c
* @param zero of type c
* @param r reducer from ((k1,k2),c) to d
* @param X left input of type {a}
* @param Y right input of type {b}
* @return a value of type {d}
*/
public static Bag groupByJoin ( final Function kx, final Function ky,
final Function gx, final Function gy,
final Function acc, MRData zero,
final Function r,
final Bag X, final Bag Y ) {
Bag s = groupBy(hash_join(kx,ky,
new Function() {
public MRData eval ( final MRData e ) {
Tuple t = (Tuple)e;
return new Tuple(new Tuple(gx.eval(t.first()),gy.eval(t.second())),t);
} },
X,Y));
Bag res = new Bag();
for ( MRData z: s ) {
Tuple t = (Tuple)z;
MRData v = zero;
for ( MRData x: (Bag)t.second() )
v = acc.eval(new Tuple(v,x));
res.add(r.eval(new Tuple(t.first(),v)));
};
return res;
}
private static void flush_table ( final Map<MRData,MRData> hashTable, final Function r, final Bag result ) {
Tuple pair = new Tuple(2);
for ( Map.Entry<MRData,MRData> entry: hashTable.entrySet() ) {
pair.set(0,entry.getKey());
pair.set(1,entry.getValue());
result.add(r.eval(pair));
};
hashTable.clear();
}
/** An equi-join combined with a group-by implemented using a sort-merge join
combined with hash-based groupby/aggregation
* @param kx left key function from a to k
* @param ky right key function from b to k
* @param gx group-by key function from a to k1
* @param gy group-by key function from b to k2
* @param acc accumulator from (c,(a,b)) to c
* @param zero of type c
* @param r reducer from ((k1,k2),c) to d
* @param X left input of type {a}
* @param Y right input of type {b}
* @return a value of type {d}
*/
final public static Bag mergeGroupByJoin ( final Function kx, final Function ky,
final Function gx, final Function gy,
final Function acc, MRData zero,
final Function r,
Bag X, Bag Y ) {
Bag tbag = new Bag(2);
Tuple pair = new Tuple(2);
pair.set(1,new Tuple(2));
final Map<MRData,MRData> hashTable = new HashMap<MRData,MRData>(1000);
Bag xs = map(new Function() {
public MRData eval ( final MRData e ) {
Tuple t = (Tuple)e;
return new Tuple(new Tuple(t.first(),kx.eval(t.second())),t.second());
} }, X);
xs.sort();
X = null;
Bag ys = map(new Function() {
public MRData eval ( final MRData e ) {
Tuple t = (Tuple)e;
return new Tuple(new Tuple(t.first(),ky.eval(t.second())),t.second());
} }, Y);
ys.sort();
Y = null;
Bag res = new Bag();
MRData partition = null;
int i = 0;
int j = 0;
while ( i < xs.size() && j < ys.size() ) {
Tuple x = (Tuple)xs.get(i);
Tuple y = (Tuple)ys.get(j);
int cmp = x.first().compareTo(y.first());
if ( cmp > 0 ) j++;
else if ( cmp < 0 ) i++;
else { // when cmp == 0
Tuple key = (Tuple)x.first();
if (partition == null)
partition = key.first();
else if (!partition.equals(key.first())) {
partition = key.first();
flush_table(hashTable,r,res);
};
int k = i;
int l = j;
for ( ; k < xs.size() && key.compareTo(((Tuple)xs.get(k)).first()) == 0; k++ ) {
MRData xx = ((Tuple)xs.get(k)).second();
for ( l = j; l < ys.size() && key.compareTo(((Tuple)ys.get(l)).first()) == 0; l++ ) {
MRData yy = ((Tuple)ys.get(l)).second();
Tuple gkey = new Tuple(gx.eval(xx),gy.eval(yy));
MRData old = hashTable.get(gkey);
if (old == null)
old = zero;
pair.set(0,old);
((Tuple)pair.get(1)).set(0,xx).set(1,yy);
hashTable.put(gkey,acc.eval(pair));
}
};
i = k;
j = l;
}
};
flush_table(hashTable,r,res);
return res;
}
/** An equi-join combined with a group-by implemented using hashing
* @param kx left key function from a to k
* @param ky right key function from b to k
* @param gx group-by key function from a to k1
* @param gy group-by key function from b to k2
* @param acc accumulator from (c,(a,b)) to c
* @param zero of type c
* @param r reducer from ((k1,k2),c) to d
* @param X left input of type {a}
* @param Y right input of type {b}
* @return a value of type {d}
*/
final public static Bag mergeGroupByJoin2 ( final Function kx, final Function ky,
final Function gx, final Function gy,
final Function acc, MRData zero,
final Function r,
Bag X, Bag Y ) {
Bag tbag = new Bag(2);
Tuple pair = new Tuple(2);
pair.set(1,new Tuple(2));
final Map<MRData,MRData> hashTable = new HashMap<MRData,MRData>(1000);
Bag xs = groupBy(map(new Function() {
public MRData eval ( final MRData e ) {
Tuple t = (Tuple)e;
return new Tuple(new Tuple(t.first(),kx.eval(t.second())),t.second());
} }, X));
Bag ys = groupBy(map(new Function() {
public MRData eval ( final MRData e ) {
Tuple t = (Tuple)e;
return new Tuple(new Tuple(t.first(),ky.eval(t.second())),t.second());
} }, Y));
X = null; Y = null;
Bag res = new Bag();
final Iterator<MRData> xi = xs.iterator();
final Iterator<MRData> yi = ys.iterator();
if ( !xi.hasNext() || !yi.hasNext() )
return res;
Tuple x = (Tuple)xi.next();
Tuple y = (Tuple)yi.next();
MRData partition = null;
while ( xi.hasNext() && yi.hasNext() ) {
int cmp = x.first().compareTo(y.first());
if (cmp < 0) { x = (Tuple)xi.next(); continue; };
if (cmp > 0) { y = (Tuple)yi.next(); continue; };
if (partition == null)
partition = ((Tuple)x.first()).first();
else if (!partition.equals(((Tuple)x.first()).first())) {
partition = ((Tuple)x.first()).first();
flush_table(hashTable,r,res);
};
for ( MRData xx: (Bag)x.second() )
for ( MRData yy: (Bag)y.second() ) {
Tuple key = new Tuple(gx.eval(xx),gy.eval(yy));
MRData old = hashTable.get(key);
if (old == null)
old = zero;
pair.set(0,old);
((Tuple)pair.get(1)).set(0,xx).set(1,yy);
hashTable.put(key,acc.eval(pair));
};
if (xi.hasNext())
x = (Tuple)xi.next();
if (yi.hasNext())
y = (Tuple)yi.next();
};
flush_table(hashTable,r,res);
return res;
}
/** repeat the loop until all termination conditions are true or until we reach the max num of steps
* @param loop a function from {a} to {(a,boolean)}
* @param init the initial value of type {a}
* @param max_num the maximum number of steps
* @return a value of type {a}
*/
public static Bag repeat ( final Function loop,
final Bag init,
final int max_num ) throws Exception {
boolean cont;
int i = 0;
Bag s = init;
s.materializeAll();
do {
MRData d = loop.eval(s);
i++;
cont = false;
if (d instanceof Bag) {
Bag bag = (Bag) d;
bag.materialize();
s.clear();
for ( MRData x: bag ) {
Tuple t = (Tuple)x;
cont |= ((MR_bool)t.second()).get();
s.add(t.first());
}
} else if (d instanceof MR_dataset) {
DataSet ds = ((MR_dataset)d).dataset();
s.clear();
int c = 0;
for ( MRData x: Plan.collect(ds) ) {
Tuple t = (Tuple)x;
if (((MR_bool)t.second()).get()) {
c++;
cont = true;
};
s.add(t.first());
};
if (!Config.testing)
System.err.println("*** Repeat #"+i+": "+c+" true results");
} else throw new Error("Wrong repeat");
} while (cont && i < max_num);
return s;
}
/** transitive closure: repeat the loop until the new set is equal to the previous set
* or until we reach the max num of steps
* @param loop a function from {a} to {a}
* @param init the initial value of type {a}
* @param max_num the maximum number of steps
* @return a value of type {a}
*/
public static Bag closure ( final Function loop,
final Bag init,
final int max_num ) throws Exception {
int i = 0;
long n = 0;
long old = 0;
Bag s = init;
s.materializeAll();
do {
MRData d = loop.eval(s);
i++;
if (d instanceof Bag) {
s = (Bag)d;
s.materialize();
old = n;
n = s.size();
} else if (d instanceof MR_dataset) {
DataSet ds = ((MR_dataset)d).dataset();
if (!Config.testing)
System.err.println("*** Repeat #"+i+": "+(ds.records-n)+" new records");
old = n;
n = ds.records;
s = Plan.collect(ds);
} else throw new Error("Wrong repeat");
} while (old < n && i < max_num);
return s;
}
/** repetition: repeat the loop until we reach the num of steps
* @param loop a function from ({a1},...,{ak}) to ({a1},...,{ak})
* @param init the initial value of type ({a1},...,{ak})
* @param num the number of steps
* @return a value of type ({a1},...,{ak})
*/
public static Tuple loop ( final Function loop,
final Tuple init,
final int num ) {
try {
Tuple s = init;
s.materializeAll();
for ( int i = 0; i < num; i++ ) {
Tuple d = (Tuple)loop.eval(s);
for ( int j = 0; j < d.size(); j++ )
if (d.get(j) instanceof Bag) {
s.set(j,d.get(i));
((Bag)d.get(j)).materialize();
} else if (d.get(j) instanceof MR_dataset) {
DataSet ds = ((MR_dataset)d.get(j)).dataset();
s.set(j,Plan.collect(ds));
} else throw new Error("Wrong loop");
};
return s;
} catch (Exception ex) {
throw new Error(ex);
}
}
/** parse a text document using a given parser
* @param parser the parser
* @param path the text document (local file or directory of files)
* @param args the arguments to pass to the parser
* @return a lazy bag that contains the parsed data
*/
public static Bag parsedSource ( final Parser parser,
final String path,
Trees args ) {
parser.initialize(args);
File file = new File(path);
final File[] files = (file.isDirectory())
? file.listFiles()
: new File[]{file};
final int dl = files.length;
parser.open(files[0].toString());
return new Bag(new BagIterator () {
Iterator<MRData> iter;
int i = 0;
String line;
public boolean hasNext () {
while (iter == null || !iter.hasNext()) {
line = parser.slice();
while (line == null)
if (++i < dl)
parser.open(files[i].toString());
else return false;
iter = parser.parse(line).iterator();
};
return true;
}
public MRData next () {
return iter.next();
}
});
}
/** parse a text document using a given parser
* @param parser the name of the parser
* @param file the text document (local file)
* @param args the arguments to pass to the parser
* @return a lazy bag that contains the parsed data
*/
public static Bag parsedSource ( String parser, String file, Trees args ) {
try {
return parsedSource(DataSource.parserDirectory.get(parser).newInstance(),file,args);
} catch (Exception e) {
throw new Error(e);
}
}
private static Bag add_source_num ( int source_num, Bag input ) {
return new Bag(new Tuple(new MR_int(source_num),input));
}
/** parse a text document using a given parser and tag output data with a source num
* @param source_num the source id
* @param parser the parser
* @param file the text document (local file)
* @param args the arguments to pass to the parser
* @return a lazy bag that contains the parsed data taged with the source id
*/
public static Bag parsedSource ( int source_num,
Parser parser,
String file,
Trees args ) {
return add_source_num(source_num,parsedSource(parser,file,args));
}
/** parse a text document using a given parser and tag output data with a source num
* @param source_num the source id
* @param parser the name of the parser
* @param file the text document (local file)
* @param args the arguments to pass to the parser
* @return a lazy bag that contains the parsed data taged with the source id
*/
public static Bag parsedSource ( int source_num, String parser, String file, Trees args ) {
try {
return parsedSource(source_num,DataSource.parserDirectory.get(parser).newInstance(),file,args);
} catch (Exception e) {
throw new Error(e);
}
}
/** aggregate the Bag elements
* @param accumulator a function from (b,a) to b
* @param zero a value of type b
* @param s a Bag of type {a}
* @return a value of type b
*/
public static MRData aggregate ( final Function accumulator,
final MRData zero,
final Bag s ) {
MRData result = zero;
for ( MRData x: s )
result = accumulator.eval(new Tuple(result,x));
return result;
}
public static MRData materialize ( MRData x ) {
if (x instanceof Bag)
((Bag)x).materialize();
return x;
}
/** Dump the value of some type to a binary local file;
* The type is dumped to a separate file.type
*/
public static void dump ( String file, Tree type, MRData value ) throws IOException {
File f = new File(file);
File parent = f.getParentFile();
if (parent != null && !parent.exists())
parent.mkdirs();
PrintStream ftp = new PrintStream(file+".type");
ftp.print("1@"+type.toString()+"\n");
ftp.close();
DataOutputStream out = new DataOutputStream(new FileOutputStream(new File(file)));
value.write(out);
out.close();
}
/** return the type of the dumped binary local file from file.type */
public static Tree get_type ( String file ) {
try {
BufferedReader ftp = new BufferedReader(new FileReader(new File(file+".type")));
String s[] = ftp.readLine().split("@");
ftp.close();
if (s.length != 2)
return null;
if (!s[0].equals("1"))
throw new Error("The binary file has been created in hadoop mode and cannot be read in java mode");
return Tree.parse(s[1]);
} catch (Exception e) {
return null;
}
}
/** read the contents of a dumped local binary file */
public static MRData read_binary ( String file ) {
try {
Tree type = get_type(file);
DataInputStream in = new DataInputStream(new FileInputStream(new File(file)));
return MRContainer.read(in);
} catch (Exception e) {
return null;
}
}
/** read the contents of a dumped local binary file and tag data with a source num */
public static Bag read_binary ( int source_num, String file ) {
return add_source_num(source_num,(Bag)read_binary(file));
}
/** generate a lazy bag of long numbers {min...max} */
public static Bag generator ( final long min, final long max ) {
if (min > max)
throw new Error("Min value ("+min+") is larger than max ("+max+") in generator");
return new Bag(new BagIterator() {
long index = min;
public boolean hasNext () {
return index <= max;
}
public MRData next () {
return new MR_long(index++);
}
});
}
/** generate a lazy bag of long numbers {min...max} and tag each lon number with a source num */
public static Bag generator ( int source_num, final long min, final long max ) {
return add_source_num(source_num,generator(min,max));
}
/** the cache that holds all local data in memory */
private static Tuple cache;
/** return the cache element at location loc */
public static MRData getCache ( int loc ) {
return cache.get(loc);
}
/** set the cache element at location loc to value and return ret */
public static MRData setCache ( int loc, MRData value, MRData ret ) {
if (value instanceof Bag)
materialize((Bag)value);
cache.set(loc,value);
return ret;
}
/** The BSP operation
* @param source the source ids of the input Bags
* @param superstep the BSP superstep is a function from ({M},S) to ({M},S,boolean)
* @param init_state is the initial state of type S
* @param order do we need to order the result?
* @param inputs the input Bags
* @return return a Bag in cache[0]
*/
public static MRData BSP ( final int[] source,
final Function superstep,
final MRData init_state,
boolean order,
final Bag[] inputs ) {
Bag msgs = new Bag();
MRData state = init_state;
Tuple result;
boolean exit;
boolean skip = false;
String tabs = "";
int step = 0;
cache = new Tuple(100);
for ( int i = 0; i < 100; i++ )
cache.set(i,new Bag());
for ( Bag x: inputs ) {
Tuple p = (Tuple)(x.get(0));
cache.set(((MR_int)p.first()).get(),
materialize(p.second()));
};
do {
if (!skip)
step++;
if (!skip && Config.trace_execution) {
tabs = Interpreter.tabs(Interpreter.tab_count);
System.out.println(tabs+" Superstep "+step+":");
System.out.println(tabs+" messages: "+msgs);
System.out.println(tabs+" state: "+state);
for ( int i = 0; i < cache.size(); i++)
if (cache.get(i) instanceof Bag && ((Bag)cache.get(i)).size() > 0)
System.out.println(tabs+" cache "+i+": "+cache.get(i));
};
result = (Tuple)superstep.eval(new Tuple(cache,msgs,state,new MR_string("")));
Bag new_msgs = (Bag)result.get(0);
state = result.get(1);
exit = ((MR_bool)result.get(2)).get();
skip = new_msgs == SystemFunctions.bsp_empty_bag;
if ((!skip || exit) && Config.trace_execution)
System.out.println(tabs+" result: "+result);
final Iterator<MRData> iter = new_msgs.iterator();
msgs = new Bag(new BagIterator() {
public boolean hasNext () {
return iter.hasNext();
}
public MRData next () {
return ((Tuple)iter.next()).get(1);
}
});
} while (!exit);
MRData[] data = new MRData[source.length];
for ( int i = 0; i < data.length; i++ )
data[i] = getCache(source[i]);
if (order && data[0] instanceof Bag) {
final Iterator<MRData> iter = ((Bag)data[0]).iterator();
return new Bag(new BagIterator() {
public boolean hasNext () {
return iter.hasNext();
}
public MRData next () {
return ((Tuple)iter.next()).get(0);
}
});
};
if (data.length == 1)
return data[0];
else return new Tuple(data);
}
}