diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java index 8a55565f261df940882ccef50c475c1111ac21b1..b9872eff5c71a086460ac1ff5db0f35534747a82 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java @@ -18,12 +18,10 @@ import com.oracle.truffle.r.runtime.rng.RandomNumberNode; public final class Rnorm implements RandFunction2_Double { private static final double BIG = 134217728; - private double[] randomVals; @Override public void init(int length, RandomNumberNode randNode) { RRNG.getRNGState(); - randomVals = randNode.executeDouble(length * 2); } @Override @@ -34,7 +32,7 @@ public final class Rnorm implements RandFunction2_Double { @Override public double evaluate(int index, double mu, double sigma, double random, RandomNumberNode randomNode) { // TODO: GnuR invokes norm_rand to get "rand" - double u1 = (int) (BIG * randomVals[index * 2]) + randomVals[index * 2 + 1]; + double u1 = (int) (BIG * randomNode.executeSingleDouble()) + randomNode.executeSingleDouble(); double rand = Random2.qnorm5(u1 / BIG, 0.0, 1.0, true, false); return rand * sigma + mu; } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Runif.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Runif.java index c5fb0cdcd9a67cbf042ef4037957042254b5e3ca..0536885f7ec6dbdd5a694c6a4ef9891d8b50ebbe 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Runif.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Runif.java @@ -30,7 +30,7 @@ public final class Runif implements RandFunction2_Double { @Override public boolean hasCustomRandomGeneration() { - return false; + return true; } @Override @@ -38,6 +38,6 @@ public final class Runif implements RandFunction2_Double { if (!RRuntime.isFinite(min) || !RRuntime.isFinite(max) || max < min) { return StatsUtil.mlError(); } - return min + random * (max - min); + return min + randomNode.executeSingleDouble() * (max - min); } } diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper.java index 10366ff26f49c8348d3c1b6a473b93cc06fddc44..5849eeabcc7cd2fcd6e7d881864ba24a800d6020 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper.java @@ -1279,7 +1279,7 @@ public class CallRFFIHelper { if (RFFIUtils.traceEnabled()) { RFFIUtils.traceUpCall("putRNGstate"); } - RRNG.updateDotRandomSeed(); + RRNG.putRNGState(); } public static double unifRand() { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java index 54f73039c58be23c64efd44a15019eaff5f19c71..38c7c2c87dad331f515cb30d81b1f9520637bb23 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java @@ -491,7 +491,7 @@ public final class RError extends RuntimeException { SAME_NUMBER_ROWS("'%s' and '%s' must have the same number of rows"), EXACT_SINGULARITY("exact singularity in '%s'"), SINGULAR_SOLVE("singular matrix '%s' in solve"), - SEED_TYPE(".Random.seed is not an integer vector but of type '%s'"), + SEED_TYPE("'.Random.seed' is not an integer vector but of type '%s', so ignored"), INVALID_NORMAL_TYPE_IN_RGNKIND("invalid Normal type in 'RNGkind'"), INVALID_USE("invalid use of '%s'"), FORMAL_MATCHED_MULTIPLE("formal argument \"%s\" matched by multiple actual arguments"), diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RNGInitAdapter.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RNGInitAdapter.java index 12918a1ed0ee2629f5a3919fd483bbc2c5dc28b7..e0eeb827214237504345ef90b0d8fe2f98849e83 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RNGInitAdapter.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RNGInitAdapter.java @@ -11,10 +11,10 @@ */ package com.oracle.truffle.r.runtime.rng; -import com.oracle.truffle.r.runtime.rng.RRNG.RandomNumberGenerator; - public abstract class RNGInitAdapter implements RandomNumberGenerator { + protected static final double I2_32M1 = 2.3283064365386963e-10; + // TODO: it seems like GNU R this is shared between the generators (does it matter?) protected final int[] iSeed = new int[625]; @@ -25,4 +25,18 @@ public abstract class RNGInitAdapter implements RandomNumberGenerator { } fixupSeeds(false); } + + /** + * Ensure 0 and 1 are never returned from rand generation algorithm. + */ + protected static double fixup(double x) { + /* transcribed from GNU R, RNG.c (fixup) */ + if (x <= 0.0) { + return 0.5 * I2_32M1; + } + // removed fixup for 1.0 since x is in [0,1). + // TODO Since GnuR does include this is should we not for compatibility (probably). + // if ((1.0 - x) <= 0.0) return 1.0 - 0.5 * I2_32M1; + return x; + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java index cd34827aaab07653be5e5c24b47a439684f1118c..56ab040453ffb9cfe867a2aed314fdfccd1876f8 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java @@ -16,7 +16,6 @@ import java.util.function.Supplier; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RDataFactory; @@ -26,7 +25,6 @@ import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPromise; import com.oracle.truffle.r.runtime.data.RTypedValue; import com.oracle.truffle.r.runtime.env.REnvironment; -import com.oracle.truffle.r.runtime.env.REnvironment.PutException; import com.oracle.truffle.r.runtime.ffi.RFFIFactory; import com.oracle.truffle.r.runtime.rng.mm.MarsagliaMulticarry; import com.oracle.truffle.r.runtime.rng.mt.MersenneTwister; @@ -101,28 +99,7 @@ public class RRNG { private static final Kind DEFAULT_KIND = Kind.MERSENNE_TWISTER; private static final NormKind DEFAULT_NORM_KIND = NormKind.INVERSION; private static final String RANDOM_SEED = ".Random.seed"; - public static final double I2_32M1 = 2.3283064365386963e-10; private static final double UINT_MAX = (double) Integer.MAX_VALUE * 2; - @CompilationFinal private static final int[] NO_SEEDS = new int[0]; - - /** - * The (logically private) interface that a random number generator must implement. - */ - public interface RandomNumberGenerator { - void init(int seed); - - void fixupSeeds(boolean initial); - - int[] getSeeds(); - - double[] genrandDouble(int count); - - Kind getKind(); - - int getNSeed(); - - void setISeed(int[] seeds); - } public static final class ContextStateImpl implements RContext.ContextState { private RandomNumberGenerator currentGenerator; @@ -149,10 +126,11 @@ public class RRNG { */ @TruffleBoundary void updateCurrentGenerator(RandomNumberGenerator newRng, boolean saveState) { - this.currentGenerator = newRng; this.allGenerators[newRng.getKind().ordinal()] = newRng; if (saveState) { - getRNGState(); + getRNGState(); // updates this.currentGenerator + // Check if the what will be soon previous generator is not corrupted, otherwise + // generate the seed again for our newRng double u = unifRand(); if (u < 0.0 || u > 1.0) { RError.warning(RError.NO_CALLER, RError.Message.GENERIC, "someone corrupted the random-number generator: re-initializing"); @@ -160,8 +138,10 @@ public class RRNG { } else { initGenerator(newRng, (int) (u * UINT_MAX)); } - updateDotRandomSeed(); - + this.currentGenerator = newRng; + putRNGState(); + } else { + this.currentGenerator = newRng; } } @@ -184,8 +164,7 @@ public class RRNG { void updateCurrentNormKind(NormKind normKind, boolean saveState) { currentNormKind = normKind; if (saveState) { - getRNGState(); - updateDotRandomSeed(); + putRNGState(); } } @@ -223,7 +202,7 @@ public class RRNG { * Ask the current generator for a random double. (cf. {@code unif_rand} in RNG.c. */ public static double unifRand() { - return currentGenerator().genrandDouble(1)[0]; + return currentGenerator().genrandDouble(); } /** @@ -239,7 +218,7 @@ public class RRNG { public static void doSetSeed(int seed, int kindAsInt, int normKindAsInt) { getRNGKind(RNull.instance); changeKindsAndInitGenerator(seed, kindAsInt, normKindAsInt); - updateDotRandomSeed(); + putRNGState(); } /** @@ -279,18 +258,6 @@ public class RRNG { } // otherwise the current one stays } - public static double fixup(double x) { - /* transcribed from GNU R, RNG.c (fixup) */ - /* ensure 0 and 1 are never returned */ - if (x <= 0.0) { - return 0.5 * I2_32M1; - } - // removed fixup for 1.0 since x is in [0,1). - // TODO Since GnuR does include this is should we not for compatibility (probably). - // if ((1.0 - x) <= 0.0) return 1.0 - 0.5 * I2_32M1; - return x; - } - private static Kind intToKind(int kindAsInt) { if (kindAsInt < 0 || kindAsInt >= Kind.VALUES.length || !Kind.VALUES[kindAsInt].isAvailable()) { throw RError.error(RError.NO_CALLER, RError.Message.RNG_NOT_IMPL_KIND, kindAsInt); @@ -321,19 +288,6 @@ public class RRNG { return seed; } - @TruffleBoundary - public static void updateDotRandomSeed() { - int[] seeds = currentGenerator().getSeeds(); - int lenSeeds = currentGenerator().getNSeed(); - int[] data = new int[lenSeeds + 1]; - data[0] = currentKind().ordinal() + 100 * currentNormKind().ordinal(); - for (int i = 0; i < lenSeeds; i++) { - data[i + 1] = seeds[i]; - } - RIntVector vector = RDataFactory.createIntVector(data, RDataFactory.COMPLETE_VECTOR); - REnvironment.globalEnv().safePut(RANDOM_SEED, vector); - } - /** * Create a random integer. */ @@ -354,6 +308,10 @@ public class RRNG { getContextState().updateCurrentNormKind(DEFAULT_NORM_KIND, false); } + /** + * Sets the current generator according to the flag under index 0 of given vector + * {@code seedsObj}. + */ private static void getRNGKind(Object seedsObj) { Object seeds = seedsObj; if (seeds == RNull.instance) { @@ -371,12 +329,13 @@ public class RRNG { } else { assert seeds != RMissing.instance; assert seeds instanceof RTypedValue; - RError.warning(RError.NO_CALLER, RError.Message.SEED_TYPE, "'.Random.seed' is not an integer vector but of type '%s', so ignored", ((RTypedValue) seeds).getRType().getName()); + RError.warning(RError.NO_CALLER, RError.Message.SEED_TYPE, ((RTypedValue) seeds).getRType().getName()); handleInvalidSeed(); return; } if (tmp == RRuntime.INT_NA || tmp < 0 || tmp > 1000) { - RError.warning(RError.NO_CALLER, RError.Message.GENERIC, "'.Random.seed[1]' is not a valid integer, so ignored"); + String type = seeds instanceof RTypedValue ? ((RTypedValue) seeds).getRType().getName() : "unknown"; + RError.warning(RError.NO_CALLER, RError.Message.SEED_TYPE, type); handleInvalidSeed(); return; } @@ -404,6 +363,11 @@ public class RRNG { getContextState().updateCurrentNormKind(newNormKind, false); } + /** + * Loads the state of RNG from global environment variable {@code .Random.seed}. This should be + * invoked before any random numbers generation as the user may have directly changed the + * {@code .Random.seed} variable and the current generator and/or its seed should be updated. + */ @TruffleBoundary public static void getRNGState() { Object seedsObj = getDotRandomSeed(); @@ -418,7 +382,8 @@ public class RRNG { RIntVector seedsVec = (RIntVector) seedsObj; seeds = seedsVec.getDataWithoutCopying(); } else { - throw RInternalError.shouldNotReachHere(); + // seedsObj is not valid, which should have been reported and fixed in getRNGKind + return; } if (seeds.length > 1 && seeds.length < (currentGenerator().getNSeed() + 1)) { @@ -432,22 +397,33 @@ public class RRNG { } } + /** + * Saves the state of RNG into global environment under {@code .Random.seed}. This should be + * invoked after any random numbers generation. + */ @TruffleBoundary public static void putRNGState() { - int lenSeed = getContextState().currentGenerator.getNSeed(); - int[] seeds = new int[lenSeed + 1]; - seeds[0] = currentKindAsInt() + 100 * currentNormKindAsInt(); - int[] currentGeneratorSeeds = currentGenerator().getSeeds(); - for (int i = 0; i < lenSeed; i++) { - seeds[i + 1] = currentGeneratorSeeds[i]; + int[] seeds = currentGenerator().getSeeds(); + int lenSeeds = currentGenerator().getNSeed(); + + // we update the existing vector from global env if possible + int[] data; + Object prevState = getDotRandomSeed(); + boolean canReusePrev = prevState instanceof RIntVector && ((RIntVector) prevState).getLength() == lenSeeds + 1 && !((RIntVector) prevState).isShared(); + if (canReusePrev) { + data = ((RIntVector) prevState).getDataWithoutCopying(); + } else { + data = new int[lenSeeds + 1]; + } + + data[0] = currentKind().ordinal() + 100 * currentNormKind().ordinal(); + for (int i = 0; i < lenSeeds; i++) { + data[i + 1] = seeds[i]; } - currentGenerator().setISeed(seeds); - RIntVector seedsVector = RDataFactory.createIntVector(seeds, true); - try { - REnvironment.globalEnv().put(RANDOM_SEED, seedsVector); - } catch (PutException e) { - throw RInternalError.shouldNotReachHere("Cannot set .Random.seed in global environment"); + if (!canReusePrev) { + RIntVector vector = RDataFactory.createIntVector(data, RDataFactory.COMPLETE_VECTOR); + REnvironment.globalEnv().safePut(RANDOM_SEED, vector); } } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberGenerator.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..3ca1a0f164238b5dd15d32ab1029ce9147346602 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberGenerator.java @@ -0,0 +1,40 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 1995-2012, The R Core Team + * Copyright (c) 2003, The R Foundation + * Copyright (c) 2014, 2016, Oracle and/or its affiliates + * + * All rights reserved. + */ +package com.oracle.truffle.r.runtime.rng; + +import com.oracle.truffle.r.runtime.rng.RRNG.Kind; + +/** + * Interface for a random number generator algorithm. + */ +public interface RandomNumberGenerator { + void init(int seed); + + void fixupSeeds(boolean initial); + + int[] getSeeds(); + + double genrandDouble(); + + Kind getKind(); + + /** + * Returns number of seed values this generator requires. + */ + int getNSeed(); + + /** + * Sets array which contains current generator flag under index 0 and seeds for random + * generation under the other indices. + */ + void setISeed(int[] seeds); +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberNode.java index f77dda2729d47d7406f9bc68719cc8e71a8e2faa..fa41460b92916fa4b52d888a6f622800bb128076 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberNode.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberNode.java @@ -31,11 +31,15 @@ public final class RandomNumberNode extends RBaseNode { private final ValueProfile generatorClassProfile = ValueProfile.createClassProfile(); public double[] executeDouble(int count) { - return generatorClassProfile.profile(generatorProfile.profile(RRNG.currentGenerator())).genrandDouble(count); + double[] result = new double[count]; + for (int i = 0; i < count; i++) { + result[i] = executeSingleDouble(); + } + return result; } public double executeSingleDouble() { - return generatorClassProfile.profile(generatorProfile.profile(RRNG.currentGenerator())).genrandDouble(1)[0]; + return generatorClassProfile.profile(generatorProfile.profile(RRNG.currentGenerator())).genrandDouble(); } public static RandomNumberNode create() { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mm/MarsagliaMulticarry.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mm/MarsagliaMulticarry.java index 85cde953ade8f95162da7b1bbe29f0fa5efdae11..697e3c65ac7ca598bbbd35eef9571477566915a8 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mm/MarsagliaMulticarry.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mm/MarsagliaMulticarry.java @@ -13,7 +13,6 @@ package com.oracle.truffle.r.runtime.rng.mm; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.rng.RNGInitAdapter; -import com.oracle.truffle.r.runtime.rng.RRNG; import com.oracle.truffle.r.runtime.rng.RRNG.Kind; /** @@ -49,20 +48,16 @@ public final class MarsagliaMulticarry extends RNGInitAdapter { } @Override - public double[] genrandDouble(int count) { + public double genrandDouble() { int state0 = iSeed[0]; int state1 = iSeed[1]; - double[] result = new double[count]; - for (int i = 0; i < count; i++) { - state0 = 36969 * (state0 & 0177777) + (state0 >>> 16); - state1 = 18000 * (state1 & 0177777) + (state1 >>> 16); - int x = (state0 << 16) ^ (state1 & 0177777); - double d = (x & 0xffffffffL) * RRNG.I2_32M1; - result[i] = RRNG.fixup(d); /* in [0,1) */ - } + state0 = 36969 * (state0 & 0177777) + (state0 >>> 16); + state1 = 18000 * (state1 & 0177777) + (state1 >>> 16); + int x = (state0 << 16) ^ (state1 & 0177777); + double d = (x & 0xffffffffL) * I2_32M1; iSeed[0] = state0; iSeed[1] = state1; - return result; + return fixup(d); /* in [0,1) */ } @Override diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mt/MersenneTwister.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mt/MersenneTwister.java index ddc4d8a7d864fc4e840ee814dcbaaea072e56167..b084498c040d708c01f8e64c056f9a29a29ff9c2 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mt/MersenneTwister.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mt/MersenneTwister.java @@ -80,6 +80,15 @@ public final class MersenneTwister extends RNGInitAdapter { private static final int TEMPERING_MASK_B = 0x9d2c5680; private static final int TEMPERING_MASK_C = 0xefc60000; + /* + * This generator can efficiently generate many random numbers in one go, upon each call to + * genrandDouble() we take the next random value from 'buffer'. If the buffer is empty, we fill + * it. + */ + private static final int BUFFER_SIZE = N; + private final double[] buffer = new double[BUFFER_SIZE]; + private int bufferIndex = BUFFER_SIZE; + /** * The array for the state vector. In GnuR, the state array is common to all algorithms (named * {@code dummy}), and the zero'th element is the number of seeds, but the algorithm uses @@ -96,14 +105,19 @@ public final class MersenneTwister extends RNGInitAdapter { // to keep variable naming (somewhat) consistent with GNU R private int[] dummy = iSeed; - /** - * Following GnuR this is set to {@code N+1} to indicate unset if MT_genrand is called, although - * that condition never appears to happen in practice, as {@code RNG_init}, cf. {@link #init} is - * always called first. N.B. This value has a relationship with {@code dummy0} in that it is - * always loaded from {@code dummy0} in {@link #genrandDouble(int)} and the updated value is - * stored back in {@code dummy[0]}. - */ - private int mti = N + 1; + @Override + public void setISeed(int[] seeds) { + boolean changed = false; + for (int i = 1; i <= getNSeed(); i++) { + changed |= iSeed[i - 1] != seeds[i]; + iSeed[i - 1] = seeds[i]; + } + fixupSeeds(false); + // kill the current buffer if seed changes + if (changed) { + bufferIndex = BUFFER_SIZE; + } + } /** * We have to recreate the effect of having the number of seeds in the array. @@ -130,6 +144,7 @@ public final class MersenneTwister extends RNGInitAdapter { iSeed[i] = seed; } fixupSeeds(true); + bufferIndex = BUFFER_SIZE; } @Override @@ -154,90 +169,63 @@ public final class MersenneTwister extends RNGInitAdapter { /** * The actual generating method, essentially transcribed from MT_genrand in GnuR RNG.c. - * Additional method have been introduced for clarity and the import - * {@link com.oracle.truffle.api.CompilerDirectives.TruffleBoundary} annotation on - * {@link #generateNewNumbers()}. */ @Override - public double[] genrandDouble(int count) { - int localDummy0 = dummy[0]; - int localMti = mti; - double[] result = new double[count]; - - localMti = localDummy0; - // It appears that this never happens - // sgenrand(4357); - RInternalError.guarantee(localMti != N + 1); - - int pos = 0; - while (true) { - int loopCount = Math.min(count - pos, N - localMti); - for (int i = 0; i < loopCount; i++) { - int y = getMt(localMti + i); - /* Tempering */ - y ^= (y >>> 11); - y ^= (y << 7) & TEMPERING_MASK_B; - y ^= (y << 15) & TEMPERING_MASK_C; - y ^= (y >>> 18); - result[pos + i] = ((y + Integer.MIN_VALUE) - (double) Integer.MIN_VALUE) * RRNG.I2_32M1; - } - for (int i = 0; i < loopCount; i++) { - result[pos + i] = RRNG.fixup(result[pos + i]); - } - localMti += loopCount; - pos += loopCount; - - if (pos == count) { - break; + public double genrandDouble() { + if (bufferIndex == BUFFER_SIZE) { + int localDummy0 = dummy[0]; + int localMti = localDummy0; + // It appears that this never happens + // sgenrand(4357); + RInternalError.guarantee(localMti != N + 1); + + int pos = 0; + while (true) { + int loopCount = Math.min(BUFFER_SIZE - pos, N - localMti); + for (int i = 0; i < loopCount; i++) { + int y = getMt(localMti + i); + /* Tempering */ + y ^= (y >>> 11); + y ^= (y << 7) & TEMPERING_MASK_B; + y ^= (y << 15) & TEMPERING_MASK_C; + y ^= (y >>> 18); + buffer[pos + i] = ((y + Integer.MIN_VALUE) - (double) Integer.MIN_VALUE) * I2_32M1; + } + for (int i = 0; i < loopCount; i++) { + buffer[pos + i] = fixup(buffer[pos + i]); + } + localMti += loopCount; + pos += loopCount; + + if (pos == BUFFER_SIZE) { + break; + } + /* generate N words at one time */ + int kk; + for (kk = 0; kk < N - M; kk++) { + int y2y = (getMt(kk) & UPPERMASK) | (getMt(kk + 1) & LOWERMASK); + setMt(kk, getMt(kk + M) ^ (y2y >>> 1) ^ mag01(y2y & 0x1)); + } + for (; kk < N - 1; kk++) { + int y2y = (getMt(kk) & UPPERMASK) | (getMt(kk + 1) & LOWERMASK); + setMt(kk, getMt(kk + (M - N)) ^ (y2y >>> 1) ^ mag01(y2y & 0x1)); + } + int y2y = (getMt(N - 1) & UPPERMASK) | (getMt(0) & LOWERMASK); + setMt(N - 1, getMt(M - 1) ^ (y2y >>> 1) ^ mag01(y2y & 0x1)); + + localMti = 0; } - /* generate N words at one time */ - int kk; - for (kk = 0; kk < N - M; kk++) { - int y2y = (getMt(kk) & UPPERMASK) | (getMt(kk + 1) & LOWERMASK); - setMt(kk, getMt(kk + M) ^ (y2y >>> 1) ^ mag01(y2y & 0x1)); - } - for (; kk < N - 1; kk++) { - int y2y = (getMt(kk) & UPPERMASK) | (getMt(kk + 1) & LOWERMASK); - setMt(kk, getMt(kk + (M - N)) ^ (y2y >>> 1) ^ mag01(y2y & 0x1)); - } - int y2y = (getMt(N - 1) & UPPERMASK) | (getMt(0) & LOWERMASK); - setMt(N - 1, getMt(M - 1) ^ (y2y >>> 1) ^ mag01(y2y & 0x1)); - - localMti = 0; + localDummy0 = localMti; + dummy[0] = localDummy0; + bufferIndex = 0; } - localDummy0 = localMti; - mti = localMti; - dummy[0] = localDummy0; - return result; + return buffer[bufferIndex++]; } private static int mag01(int v) { return (v & 1) != 0 ? MATRIXA : 0; } - @TruffleBoundary - private void generateNewNumbers() { - int y2y; - int kk; - - // It appears that this never happens - // sgenrand(4357); - RInternalError.guarantee(mti != N + 1); - - for (kk = 0; kk < N - M; kk++) { - y2y = (getMt(kk) & UPPERMASK) | (getMt(kk + 1) & LOWERMASK); - setMt(kk, getMt(kk + M) ^ (y2y >>> 1) ^ mag01(y2y & 0x1)); - } - for (; kk < N - 1; kk++) { - y2y = (getMt(kk) & UPPERMASK) | (getMt(kk + 1) & LOWERMASK); - setMt(kk, getMt(kk + (M - N)) ^ (y2y >>> 1) ^ mag01(y2y & 0x1)); - } - y2y = (getMt(N - 1) & UPPERMASK) | (getMt(0) & LOWERMASK); - setMt(N - 1, getMt(M - 1) ^ (y2y >>> 1) ^ mag01(y2y & 0x1)); - - mti = 0; - } - @Override public Kind getKind() { return Kind.MERSENNE_TWISTER; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java index 994950b30fb9b6d43ef11c791c707249dcbf507d..79a8117b3b8c6acc7554842a53d368ffc3baf2c0 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java @@ -130,12 +130,8 @@ public final class UserRNG extends RNGInitAdapter { } @Override - public double[] genrandDouble(int count) { - double[] result = new double[count]; - for (int i = 0; i < count; i++) { - result[i] = userRngRFFI.rand(); - } - return result; + public double genrandDouble() { + return userRngRFFI.rand(); } @Override diff --git a/com.oracle.truffle.r.test.cran/r/install.cran.packages.R b/com.oracle.truffle.r.test.cran/r/install.cran.packages.R index 6f6ac20367fb943b12546ca5e3f58b01cbf8d7c8..a92c95ff4a23de6e5b9ec66b9e0a38d7e6d14ade 100644 --- a/com.oracle.truffle.r.test.cran/r/install.cran.packages.R +++ b/com.oracle.truffle.r.test.cran/r/install.cran.packages.R @@ -537,6 +537,15 @@ install.suggests <- function(pkgnames) { for (pkgname in pkgnames) { suggests <- install.order(avail.pkgs, avail.pkgs[pkgname, ], "suggests") if (length(suggests) > 0) { + if (is.fastr() && !ignore.blacklist) { + # no point in trying to install blacklisted packages (which are likely) + blacklist <- get.blacklist() + nsuggests <- suggests[!suggests %in% blacklist] + if (length(nsuggests) != length(suggests)) { + cat("not installing Suggests of:", pkgname, ", one or more is blacklisted", "\n") + return() + } + } dep.status <- install.status[suggests] # three cases: # 1. all TRUE: nothing to do all already installed ok diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 768d18e36da0bccd498c40b6ee75af81d0e98550..d8b7dc03e66e39887804ac7697a2684e9e9a96be 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -114716,6 +114716,17 @@ Error: unexpected '*' in: #{ library("testrffi", lib.loc = "tmptest/com.oracle.truffle.r.test.rpackages"); a <- c(1L,2L,3L); x <- rffi.iterate_iptr(a); detach("package:testrffi"); x } [1] 1 2 3 + +[[2]] +[1] 1 2 3 4 + +[[3]] +[1] 0.0 0.2 0.4 0.6 + +[[4]] +[1] TRUE FALSE FALSE FALSE + + ##com.oracle.truffle.r.test.rffi.TestRFFIPackage.testRFFI11# #{ library("testrffi", lib.loc = "tmptest/com.oracle.truffle.r.test.rpackages"); x <- rffi.dotCModifiedArguments(c(0,1,2,3)); detach("package:testrffi"); x } [[1]] @@ -114911,6 +114922,142 @@ NULL [1] 0.45336386 0.38848030 0.94576608 0.11726267 0.21542351 0.08672997 [7] 0.35201276 0.16919220 0.93579263 0.26084486 +##com.oracle.truffle.r.test.rng.TestRRNG.testDirectReadingSeed# +#invisible(runif(1)); length(.Random.seed) +[1] 626 + +##com.oracle.truffle.r.test.rng.TestRRNG.testDirectReadingSeed# +#set.seed(42); .Random.seed + [1] 403 624 507561766 1260545903 1362917092 -1772566379 + [7] -1344458670 1072028587 -1293707536 1708789297 -1333339586 256895399 + [13] 998413756 -555191411 -1047547670 -50950813 -1543487672 -1766763351 + [19] 32923734 1971684063 1718493076 -1117926011 890393730 -1032174053 + [25] 932479648 -1892763103 -1040205458 262148887 -1220643732 1566094973 + [31] -137659622 1045161427 -1555709704 178265753 -1033943674 -1034388913 + [37] -1721830332 -1749741963 -1137867598 -2065544053 866474576 458187281 + [43] 1218274462 -2000447353 199687964 1107998061 590795082 -858760637 + [49] -240079192 839017609 -1886488906 -1479389761 1595536628 1848478565 + [55] 368165090 -1706758405 -128901632 360384001 2027084750 1372682743 + [61] -1578682932 -1716686755 1324659578 1519053491 2044463192 -906549639 + [67] 1851192294 -1175847633 -1083563612 -849984427 333580562 1871261035 + [73] 1972555184 1756407281 1943215870 -1751074969 1282021500 -1397758131 + [79] 518529450 -1421699293 488820232 -413309847 1749794070 441878687 + [85] 81427028 1971206469 -1203675838 995493851 -366646944 -810597919 + [91] 2006003246 1288196311 -111499476 -280946115 -4973606 75390867 + [97] 1671430072 -422306215 -1145056698 -393285617 1823866628 1253337653 +[103] 1712504178 1846705739 -1820069616 -990520879 247466334 -1717615033 +[109] 1633935836 -246413011 1455137290 -1652210685 741252456 1555715145 +[115] 197538678 -1312148609 -687362124 1153947429 286861730 568692923 +[121] 1575576768 1925410241 1087549582 1279039415 -1208956788 1317778461 +[127] -1306414022 257836147 1550427928 86967865 -1875779418 -520138001 +[133] 1989839972 1598521365 1842848210 -1967767765 -1806646160 -1558774351 +[139] -1040440386 1215776039 1529633596 -1637672179 -121023894 -992976669 +[145] -1867768632 -1273940951 1167448534 786779743 -2131127020 -1987943163 +[151] 563160578 1714129307 -1871376352 -1347450463 590308078 -135901545 +[157] -2080269844 1678065149 -1705673574 1784846675 -871301512 1157619225 +[163] 491069190 301147599 -563099196 -1769503243 -230115790 1806462987 +[169] 1792100304 1913393553 35614238 -1176456185 -165968740 -27190035 +[175] -1087819062 1583082947 678644776 -1957034999 760393782 778033471 +[181] -636999052 757457637 -155102622 -1134562693 -1432327296 862688641 +[187] 960447822 1400730999 -1343939764 -1742358563 2020046074 549674547 +[193] -2039609896 1111401977 -527331994 -970823505 -779241692 -1209238571 +[199] -964822382 1395462379 -170034384 -1664281231 264366206 1603506919 +[205] -1702273540 398593741 -269270230 -1017124189 1029450632 -57883671 +[211] 647280278 686937119 -443846700 1428836549 -1446924606 1958398811 +[217] -1052543264 -1494249119 1771722670 -1095103401 972246188 258286013 +[223] -1737515686 1505268499 -883376840 350445017 -1548801082 608508815 +[229] -1454615420 -957455947 -913346830 527442379 5070480 -1974335151 +[235] -142896418 122151367 1556997980 -1392643923 1564443530 1762940803 +[241] -2059486488 -1650363447 -520885002 1934835455 -757373644 1661447845 +[247] 1604991778 2071204923 -717868480 -1355580095 1725471246 -179039433 +[253] -863752692 -1438942307 -762972742 1484404723 1185490072 1357252025 +[259] 2083912230 789790319 -352083484 50673557 -426437806 1252893355 +[265] 1090056688 -1651315407 -1847300802 -625631065 -46063428 1015857805 +[271] 1696986090 -525257629 569571912 -2138041431 1476940630 1044126175 +[277] -45086060 -197788539 1234368386 1489227035 -749691488 -315664095 +[283] -1349383058 251890199 -1108361364 86033789 -1966965222 2084588755 +[289] 272055288 104766873 -876742522 -985345713 1208721220 -408355467 +[295] 346482610 -350383221 1522021712 898089233 -2087422050 1603587975 +[301] -398783972 15107181 -239168438 -680623805 -1588532824 860922761 +[307] -748033610 -1771805505 -331259916 -500352411 -1533811742 820114939 +[313] -1804945152 -35969791 -1906397490 2139123959 277741772 2022506333 +[319] -1321388422 1078120883 -1411710120 -1058724487 1071589094 -1584278481 +[325] -1648603996 743551829 1457318930 -1292372885 -597481296 -1389853455 +[331] 1025749502 2006806119 907256700 -259836339 2032231594 377765411 +[337] -47150840 -1076157591 -424628202 1686380447 1392993620 1113942085 +[343] -1178271678 -1006203173 -581139360 2049892577 433488174 377669591 +[349] 1924592172 418916669 -1039261990 1002030739 158104248 -1999528615 +[355] -768506554 1521633039 -177362428 -998811339 -1135665038 -260782773 +[361] 1087491088 1533884625 -81126306 1619492167 -1423774500 -1109731283 +[367] -43621110 -2094372093 -1687562136 -1406692535 1903470198 1834175103 +[373] 284826292 1716946469 -566342494 1852413883 1593704384 -148730687 +[379] 881951630 -4026697 1052939148 -1227209955 -984795334 568142707 +[385] -2067553768 -503577287 -934472794 1667115503 -2072529052 -544084203 +[391] 1612022994 -1916008917 -287563920 -1823613775 -968902978 -1274348505 +[397] -1212094916 -681219571 212178282 513345507 1305794504 -97651927 +[403] -1622291242 1367991647 829521940 -612854779 1930938626 675483803 +[409] -1238947040 95299745 -1926777362 -1023949417 2064180460 -559198979 +[415] 1326612378 -1041956781 -440895112 -866362087 -1278619130 172850383 +[421] -1405979452 -384207627 1766332722 288732939 962208464 -1547538287 +[427] 1929150750 1742727943 2017824668 1738206189 -1057557046 101192899 +[433] 1380550440 669401865 -405527754 -1914703809 -339372684 1799590373 +[439] -446073502 -2050295429 1806698112 852079745 -1640949682 1138388087 +[445] -639506868 -656193827 2138437626 118048051 1612906712 -1008032519 +[451] 1916780646 2050506671 -101326300 -2004489515 184475026 -1662396437 +[457] 1596184112 -675089295 -1577550978 -843167257 -1257707268 1425235405 +[463] -1066236374 1924108707 1386210952 793281257 245344662 2064476959 +[469] -1555146028 456097733 -1370795582 -1220980133 -193949216 124596321 +[475] -1371166034 -1037925545 -1180330068 -1443221315 42967130 -125699565 +[481] -1814349768 -1063330599 759619270 -1177128305 656015236 -1588637515 +[487] -2075012622 -283088693 -1961805424 1984391249 -1077172770 -1822549817 +[493] -496831908 1105641389 1026573962 -1178108285 1689253352 -2041793847 +[499] 591945718 1305105919 -412889036 750017957 1389714978 -2000282821 +[505] -1321153216 143694913 -805475058 -645395913 615250188 308808349 +[511] 276265146 -1182327053 -1934024808 913376441 1517759782 -1411377809 +[517] 418827492 1391306389 542701138 1645308331 -738571024 -1035482063 +[523] 84803646 -1032366169 548121532 -1930620531 -179816726 1283971939 +[529] 173126984 518705833 -2124003754 382644447 1935537556 491400069 +[535] 1679792770 1655264283 -285689696 -1221855199 -504340626 -2117926633 +[541] -783480212 -1901800323 1833271578 -1991199789 -1030441224 358161561 +[547] -1150768250 252520527 -521909692 -120001419 908872370 -336274805 +[553] 1018630224 -88334319 1985448606 -1061026169 1010504988 1350456173 +[559] 852645706 -1205295037 828187816 1729815177 -795779914 -1026393153 +[565] 781503220 -1503073947 2135033570 1226505467 -428846080 -1875426303 +[571] -1900641842 244017143 568380364 1462275677 1762769274 -1021921101 +[577] 424017496 -917560199 1572035046 2115349295 -1137018972 713630293 +[583] 686018322 520272747 -1273003088 1520198641 -465550082 1341531495 +[589] -1385615748 1662158157 -874076246 -1511922397 866796552 1221911145 +[595] 73507606 445494943 744509524 -1115121851 1297392450 -598534693 +[601] -1132486816 212500449 1290261550 798572247 616512812 1617639485 +[607] -537648678 -569299565 -526060104 978000985 -1695598522 1873911311 +[613] 340874500 -1149876171 1737982834 695405643 353085200 417372113 +[619] -346017954 -1916029881 -1735526436 1461823277 580724746 -622095869 +[625] -686746776 705745481 + +##com.oracle.truffle.r.test.rng.TestRRNG.testDirectSeedAssignment#Output.IgnoreWarningContext# +#.Random.seed <- c(401, 1, 2); invisible(runif(3)) +Warning message: +In runif(3) : + '.Random.seed' is not an integer vector but of type 'double', so ignored + +##com.oracle.truffle.r.test.rng.TestRRNG.testDirectSeedAssignment# +#.Random.seed <- c(401L, 1L, 2L); runif(3) +[1] 0.5641106 0.2932388 0.6696743 + +##com.oracle.truffle.r.test.rng.TestRRNG.testDirectSeedAssignment#Output.IgnoreWarningContext# +#.Random.seed <- c(999, 1, 2); invisible(runif(3)) +Warning message: +In runif(3) : + '.Random.seed' is not an integer vector but of type 'double', so ignored + +##com.oracle.truffle.r.test.rng.TestRRNG.testGeneratorChange# +#RNGkind('Marsaglia-Multicarry'); RNGkind('Mersenne-Twister'); set.seed(2); runif(5); +[1] 0.1848823 0.7023740 0.5733263 0.1680519 0.9438393 + +##com.oracle.truffle.r.test.rng.TestRRNG.testGeneratorChange# +#invisible(runif(5)); RNGkind('Marsaglia-Multicarry'); set.seed(2); runif(5); +[1] 0.8431527 0.5749091 0.1531976 0.3460321 0.2313936 + ##com.oracle.truffle.r.test.rpackages.TestRecommendedPackages.testLoad#Ignored.OutputFormatting#Context.NonShared#Context.LongTimeout# #{ library(KernSmooth, lib.loc = "tmptest/com.oracle.truffle.r.test.rpackages"); detach("package:KernSmooth"); } KernSmooth 2.23 loaded diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rng/TestRRNG.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rng/TestRRNG.java new file mode 100644 index 0000000000000000000000000000000000000000..b578868551d8e969ee14175f6d268f234077b164 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rng/TestRRNG.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.test.rng; + +import org.junit.Test; + +import com.oracle.truffle.r.test.TestBase; + +public class TestRRNG extends TestBase { + @Test + public void testDirectSeedAssignment() { + // changes generator to MarsagliaMulticarry and sets its 2 seeds + assertEval(".Random.seed <- c(401L, 1L, 2L); runif(3)"); + // wrong values: not integer + assertEval(Output.IgnoreWarningContext, ".Random.seed <- c(401, 1, 2); invisible(runif(3))"); + // wrong values: wrong generator number + assertEval(Output.IgnoreWarningContext, ".Random.seed <- c(999, 1, 2); invisible(runif(3))"); + } + + @Test + public void testGeneratorChange() { + assertEval("invisible(runif(5)); RNGkind('Marsaglia-Multicarry'); set.seed(2); runif(5);"); + assertEval("RNGkind('Marsaglia-Multicarry'); RNGkind('Mersenne-Twister'); set.seed(2); runif(5);"); + } + + @Test + public void testDirectReadingSeed() { + assertEval("invisible(runif(1)); length(.Random.seed)"); + assertEval("set.seed(42); .Random.seed"); + } +} diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides index b3fe4a42434be4ae36cad844009248ce24d0d24e..d76934e4506834d6521c3efa1b2386f4a4ef143a 100644 --- a/mx.fastr/copyrights/overrides +++ b/mx.fastr/copyrights/overrides @@ -210,6 +210,7 @@ com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mm/MarsagliaMu com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mt/MersenneTwister.java,hiroshima.copyright com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RNGInitAdapter.java,gnu_r.copyright com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java,gnu_r.copyright +com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberGenerator.java,gnu_r.copyright com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/PrimitiveMethodsInfo.java,gnu_r_gentleman_ihaka.copyright com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ROptions.java,gnu_r.copyright com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java,gnu_r.copyright diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py index 21b21917982e13eb40112575b6414fe38c9a2d4e..8b88672dec8e4eabbae35236a26d532e6f0a01c5 100644 --- a/mx.fastr/mx_fastr.py +++ b/mx.fastr/mx_fastr.py @@ -391,7 +391,7 @@ def _test_subpackage(name): return '.'.join((_test_package(), name)) def _simple_unit_tests(): - return ','.join(map(_test_subpackage, ['library.base', 'library.stats', 'library.utils', 'library.fastr', 'builtins', 'functions', 'tck', 'parser', 'S4'])) + return ','.join(map(_test_subpackage, ['library.base', 'library.stats', 'library.utils', 'library.fastr', 'builtins', 'functions', 'tck', 'parser', 'S4', 'rng'])) def _package_unit_tests(): return ','.join(map(_test_subpackage, ['rffi', 'rpackages'])) diff --git a/mx.fastr/mx_fastr_pkgs.py b/mx.fastr/mx_fastr_pkgs.py index 25bb29517c7e5affbf052eb4790a71b5df0c4033..874dbf5e651d0f7df036a77780e047dc0dff5cec 100644 --- a/mx.fastr/mx_fastr_pkgs.py +++ b/mx.fastr/mx_fastr_pkgs.py @@ -235,11 +235,27 @@ def pkgtest(args): return rc def tar_tests(testdir): - test_tar = join(_fastr_suite_dir, testdir + '.tar') - subprocess.call(['tar', 'cf', test_tar, os.path.basename(testdir)]) - if os.path.exists(test_tar + '.gz'): - os.remove(test_tar + '.gz') - subprocess.call(['gzip', test_tar]) + if os.environ.has_key('FASTR_TEST_GZIP'): + test_tar = testdir + '.tar' + subprocess.call(['tar', 'cf', test_tar, os.path.basename(testdir)]) + if os.path.exists(test_tar + '.gz'): + os.remove(test_tar + '.gz') + subprocess.call(['gzip', test_tar]) + else: + # workaround for lack of support for accessing gz files + with open(testdir + '.agg', 'w') as o: + for root, _, files in os.walk(testdir): + for f in files: + ext = os.path.splitext(f)[1] + if f == 'test_time' or f == 'testfile_status' or ext == '.pdf' or ext == '.prev' or ext == '.save': + continue + absfile = join(root, f) + relfile = relpath(absfile, _fastr_suite_dir()) + o.write('#### ' + relfile + '\n') + with open(absfile) as inp: + text = inp.read() + o.write(text) + class TestFileStatus: '''