Changeset 164

Show
Ignore:
Timestamp:
06/19/10 18:23:09 (2 years ago)
Author:
dsimcha
Message:

Add AUROC, misc. 2.047-related updates.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/alloc.d

    r159 r164  
    19551955     * sorted on.*/ 
    19561956    T find(U)(U whatToFind) { 
    1957         T* ptr = enforce( opIn_r!(U)(whatToFind), 
     1957        T* ptr = dstatsEnforce( opIn_r!(U)(whatToFind), 
    19581958            "Item not found:  " ~ to!string(whatToFind)); 
    19591959        return *ptr; 
     
    20832083 
    20842084//    TreeRange!(T, mapFun) asRange() { 
    2085 //        enforce(0, "Not implemented yet."); 
     2085//        dstatsEnforce(0, "Not implemented yet."); 
    20862086//    } 
    20872087 
  • trunk/base.d

    r163 r164  
    6565    void main (){} 
    6666} 
     67 
     68class DstatsArgumentException : Exception { 
     69    this(string msg) { 
     70        super(msg); 
     71    } 
     72} 
     73 
     74T dstatsEnforce(T, string file = __FILE__, int line = __LINE__) 
     75(T value, lazy const(char)[] msg = null) { 
     76    if(!value) { 
     77        auto exceptMsg = (msg !is null) ? msg.idup : "Invalid argument."; 
     78        throw new DstatsArgumentException(file ~ " (" ~ text(line) ~ ") :  " ~ 
     79            exceptMsg); 
     80    } 
     81 
     82    return value; 
     83} 
     84 
    6785 
    6886/** Tests whether T is an input range whose elements can be implicitly 
     
    110128 * foreach(elem; T.init) {}.*/ 
    111129template IterType(T) { 
    112     alias ReturnType!( 
     130    alias ReturnType!(typeof( 
    113131        { 
    114132            foreach(elem; T.init) { 
     
    116134            } 
    117135            assert(0); 
    118         }) IterType; 
     136        })) IterType; 
    119137} 
    120138 
     
    151169    } 
    152170    return output; 
     171} 
     172 
     173/**Given a tuple possibly containing forward ranges, returns a tuple where 
     174 * save() has been called on all forward ranges. 
     175 */ 
     176Tuple!T saveAll(T...)(T args) { 
     177    Tuple!T ret; 
     178 
     179    foreach(ti, elem; args) { 
     180        static if(isForwardRange!(typeof(elem))) { 
     181            ret.field[ti] = elem.save; 
     182        } else { 
     183            ret.field[ti] = elem; 
     184        } 
     185    } 
     186 
     187    return ret; 
    153188} 
    154189 
     
    670705} 
    671706 
     707/**Finds the area under the ROC curve (a curve with sensitivity on the Y-axis 
     708 * and 1 - specificity on the X-axis).  This is a useful metric for 
     709 * determining how well a test statistic discriminates between two classes. 
     710 * The following assumptions are made in this implementation: 
     711 * 
     712 * 1.  For some cutoff value c and test statistic T, your decision rule is of 
     713 *     the form "Class A if T > c, Class B if T < c". 
     714 * 
     715 * 2.  In the case of ties, i.e. if class A and class B both have an identical 
     716 *     value, linear interpolation is used.  This is because changing the 
     717 *     value of c infinitesimally will change both sensitivity and specificity 
     718 *     in these cases. 
     719 */ 
     720double auroc(R1, R2)(R1 classATs, R2 classBTs) 
     721if(isNumeric!(ElementType!R1) && isNumeric!(ElementType!R2)) { 
     722    mixin(newFrame); 
     723    auto classA = tempdup(classATs); 
     724    auto classB = tempdup(classBTs); 
     725    qsort(classA); 
     726    qsort(classB); 
     727 
     728    // Start cutoff at -infinity, such that we get everything in class A, i.e. 
     729    // perfect specificity, zero sensitivity.  We arbitrarily define class B 
     730    // as our "positive" class. 
     731    double tp = 0, tn = classA.length, fp = 0, fn = classB.length; 
     732    double[2] lastPoint = 0; 
     733 
     734    CommonType!(ElementType!R1, ElementType!R2) currentVal; 
     735 
     736    ElementType!R1 popA() { 
     737        tn--; 
     738        fp++; 
     739        auto ret = classA.front(); 
     740        classA.popFront(); 
     741        return ret; 
     742    } 
     743 
     744    ElementType!R2 popB() { 
     745        fn--; 
     746        tp++; 
     747        auto ret = classB.front(); 
     748        classB.popFront(); 
     749        return ret; 
     750    } 
     751 
     752    double area = 0; 
     753    while(!classA.empty && !classB.empty) { 
     754        if(classA.front() < classB.front()) { 
     755            currentVal = popA(); 
     756        } else { 
     757            currentVal = popB(); 
     758        } 
     759 
     760        // Handle ties. 
     761        while(!classA.empty && classA.front() == currentVal) { 
     762            popA(); 
     763        } 
     764 
     765        while(!classB.empty && classB.front() == currentVal) { 
     766            popB(); 
     767        } 
     768 
     769        double[2] curPoint; 
     770        curPoint[0] = 1.0 - tn / (fp + tn); 
     771        curPoint[1] = tp / (tp + fn); 
     772 
     773        immutable xDist = curPoint[0] - lastPoint[0]; 
     774        area += xDist * lastPoint[1];  // Rectangular part. 
     775        area += xDist * 0.5 * (curPoint[1] - lastPoint[1]);  // Triangular part. 
     776        lastPoint[] = curPoint[]; 
     777    } 
     778 
     779    if(classA.length > 0 && classB.length == 0) { 
     780        // Then we already have a sensitivity of 1, move straight to the right 
     781        // to the point (1, 1). 
     782 
     783        immutable xDist = 1 - lastPoint[0]; 
     784        area += xDist * lastPoint[1];  // Rectangular part. 
     785        area += xDist * 0.5 * (1 - lastPoint[1]);  // Triangular part. 
     786    } 
     787 
     788    return area; 
     789} 
     790 
     791unittest { 
     792    // Values worked out by hand on paper.  If you don't believe me, work 
     793    // them out yourself. 
     794    assert(auroc([4,5,6], [1,2,3]) == 1); 
     795    assert(approxEqual(auroc([8,6,7,5,3,0,9], [3,6,2,4,3,6]), 0.6904762)); 
     796    assert(approxEqual(auroc([2,7,1,8,2,8,1,8], [3,1,4,1,5,9,2,6]), 0.546875)); 
     797 
     798    writeln("Passed auroc unittest."); 
     799} 
     800 
    672801/// 
    673802T sign(T)(T num) pure nothrow if(is(typeof(num < 0))) { 
     
    8811010 
    8821011    /// 
    883     bool empty() const pure nothrow
     1012    bool empty() @property
    8841013        return nPerms == 0; 
    8851014    } 
  • trunk/cor.d

    r161 r164  
    3131module dstats.cor; 
    3232 
    33 import std.range, std.typecons, std.contracts, std.math, std.traits
     33import std.range, std.typecons, std.contracts, std.math, std.traits, std.typetuple
    3434 
    3535import dstats.sort, dstats.base, dstats.alloc, dstats.regress : invert; 
     
    6868        // properly aligned, this can result in about 2x speedups compared 
    6969        // to simply submitting everything to a single PearsonCor struct. 
    70         enforce(input1.length == input2.length, 
     70        dstatsEnforce(input1.length == input2.length, 
    7171            "Ranges must be same length for Pearson correlation."); 
    7272 
     
    120120        } 
    121121 
    122         enforce(input1.empty && input2.empty, 
     122        dstatsEnforce(input1.empty && input2.empty, 
    123123            "Ranges must be same length for Pearson correlation."); 
    124124    } 
     
    336336    auto ranks1 = spearmanCorRank(input1); 
    337337    auto ranks2 = spearmanCorRank(input2); 
    338     enforce(ranks1.length == ranks2.length, 
     338    dstatsEnforce(ranks1.length == ranks2.length, 
    339339        "Ranges must be same length for Spearman correlation."); 
    340340 
     
    429429if(isInputRange!(T) && isInputRange!(U)) { 
    430430    static if(isArray!(T) && isArray!(U)) { 
    431         enforce(input1.length == input2.length, 
     431        dstatsEnforce(input1.length == input2.length, 
    432432            "Ranges must be same length for Kendall correlation."); 
    433433        if(input1.length <= kendallSmallN) { 
     
    441441    scope(exit) TempAlloc.free; 
    442442 
    443     enforce(i1d.length == i2d.length, 
     443    dstatsEnforce(i1d.length == i2d.length, 
    444444        "Ranges must be same length for Kendall correlation."); 
    445445 
     
    456456 */ 
    457457double kendallCorDestructive(T, U)(T[] input1, U[] input2) { 
    458     enforce(input1.length == input2.length, 
     458    dstatsEnforce(input1.length == input2.length, 
    459459        "Ranges must be same length for Kendall correlation."); 
    460460    return kendallCorDestructiveLowLevel(input1, input2).field[0]; 
     
    569569                    m1++; 
    570570                } else { 
    571                     enforce(0, "Can't compute Kendall's Tau with NaNs."); 
     571                    dstatsEnforce(0, "Can't compute Kendall's Tau with NaNs."); 
    572572                } 
    573573            } else if(input2[i] < input2[j]) { 
     
    579579                    m1++; 
    580580                } else { 
    581                     enforce(0, "Can't compute Kendall's Tau with NaNs."); 
     581                    dstatsEnforce(0, "Can't compute Kendall's Tau with NaNs."); 
    582582                } 
    583583            } else if(input2[i] == input2[j]) { 
     
    589589                    m1++; 
    590590                } else { 
    591                     enforce(0, "Can't compute Kendall's Tau with NaNs."); 
     591                    dstatsEnforce(0, "Can't compute Kendall's Tau with NaNs."); 
    592592                } 
    593593 
    594594            } else { 
    595                 enforce(0, "Can't compute Kendall's Tau with NaNs."); 
     595                dstatsEnforce(0, "Can't compute Kendall's Tau with NaNs."); 
    596596            } 
    597597        } 
  • trunk/distrib.d

    r163 r164  
    185185/// 
    186186double uniformPDF(double X, double lower, double upper) { 
    187     enforce(X >= lower, "Can't have X < lower bound in uniform distribution."); 
    188     enforce(X <= upper, "Can't have X > upper bound in uniform distribution."); 
     187    dstatsEnforce(X >= lower, "Can't have X < lower bound in uniform distribution."); 
     188    dstatsEnforce(X <= upper, "Can't have X > upper bound in uniform distribution."); 
    189189    return 1.0L / (upper - lower); 
    190190} 
     
    192192/// 
    193193double uniformCDF(double X, double lower, double upper) { 
    194     enforce(X >= lower, "Can't have X < lower bound in uniform distribution."); 
    195     enforce(X <= upper, "Can't have X > upper bound in uniform distribution."); 
     194    dstatsEnforce(X >= lower, "Can't have X < lower bound in uniform distribution."); 
     195    dstatsEnforce(X <= upper, "Can't have X > upper bound in uniform distribution."); 
    196196 
    197197    return (X - lower) / (upper - lower); 
     
    200200/// 
    201201double uniformCDFR(double X, double lower, double upper) { 
    202     enforce(X >= lower, "Can't have X < lower bound in uniform distribution."); 
    203     enforce(X <= upper, "Can't have X > upper bound in uniform distribution."); 
     202    dstatsEnforce(X >= lower, "Can't have X < lower bound in uniform distribution."); 
     203    dstatsEnforce(X <= upper, "Can't have X > upper bound in uniform distribution."); 
    204204 
    205205    return (upper - X) / (upper - lower); 
     
    208208/// 
    209209double poissonPMF(ulong k, double lambda) { 
    210     enforce(lambda > 0, "Cannot have a Poisson with lambda <= 0 or nan."); 
     210    dstatsEnforce(lambda > 0, "Cannot have a Poisson with lambda <= 0 or nan."); 
    211211 
    212212    return exp(cast(double) k * log(lambda) - 
     
    234234/**P(K <= k) where K is r.v.*/ 
    235235double poissonCDF(ulong k, double lambda) { 
    236     enforce(lambda > 0, "Cannot have a poisson with lambda <= 0 or nan."); 
     236    dstatsEnforce(lambda > 0, "Cannot have a poisson with lambda <= 0 or nan."); 
    237237 
    238238    return (max(k, lambda) >= POISSON_NORMAL) ? 
     
    286286/**P(K >= k) where K is r.v.*/ 
    287287double poissonCDFR(ulong k, double lambda) { 
    288     enforce(lambda > 0, "Can't have a poisson with lambda <= 0 or nan."); 
     288    dstatsEnforce(lambda > 0, "Can't have a poisson with lambda <= 0 or nan."); 
    289289 
    290290    return (max(k, lambda) >= POISSON_NORMAL) ? 
     
    329329 * is closest to pVal is used.*/ 
    330330uint invPoissonCDF(double pVal, double lambda) { 
    331     enforce(lambda > 0, "Cannot have a poisson with lambda <= 0 or nan."); 
    332     enforce(pVal >= 0 && pVal <= 1, "P-values must be between 0, 1."); 
     331    dstatsEnforce(lambda > 0, "Cannot have a poisson with lambda <= 0 or nan."); 
     332    dstatsEnforce(pVal >= 0 && pVal <= 1, "P-values must be between 0, 1."); 
    333333 
    334334    // Use normal approximation to get approx answer, then brute force search. 
     
    381381/// 
    382382double binomialPMF(ulong k, ulong n, double p) { 
    383     enforce(k <= n, "k cannot be > n in binomial distribution."); 
    384     enforce(p >= 0 && p <= 1, "p must be between 0, 1 in binomial distribution."); 
     383    dstatsEnforce(k <= n, "k cannot be > n in binomial distribution."); 
     384    dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 in binomial distribution."); 
    385385    return exp(logNcomb(n, k) + k * log(p) + (n - k) * log(1 - p)); 
    386386} 
     
    416416///P(K <= k) where K is random variable. 
    417417double binomialCDF(ulong k, ulong n, double p) { 
    418     enforce(k <= n, "k cannot be > n in binomial distribution."); 
    419     enforce(p >= 0 && p <= 1, "p must be between 0, 1 in binomial distribution."); 
     418    dstatsEnforce(k <= n, "k cannot be > n in binomial distribution."); 
     419    dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 in binomial distribution."); 
    420420 
    421421    if(k == n) { 
     
    496496///P(K >= k) where K is random variable. 
    497497double binomialCDFR(ulong k, ulong n, double p) { 
    498     enforce(k <= n, "k cannot be > n in binomial distribution."); 
    499     enforce(p >= 0 && p <= 1, "p must be between 0, 1 in binomial distribution."); 
     498    dstatsEnforce(k <= n, "k cannot be > n in binomial distribution."); 
     499    dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 in binomial distribution."); 
    500500 
    501501    if(k == 0) { 
     
    570570 * is closest to pVal is used.*/ 
    571571uint invBinomialCDF(double pVal, uint n, double p) { 
    572     enforce(pVal >= 0 && pVal <= 1, "p-values must be between 0, 1."); 
    573     enforce(p >= 0 && p <= 1, "p must be between 0, 1 in binomial distribution."); 
     572    dstatsEnforce(pVal >= 0 && pVal <= 1, "p-values must be between 0, 1."); 
     573    dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 in binomial distribution."); 
    574574 
    575575    // Use normal approximation to get approx answer, then brute force search. 
     
    653653// both speed and accuracy are "good enough" for most practical purposes. 
    654654double hypergeometricCDF(long x, long n1, long n2, long n) { 
    655     enforce(x <= n, "x must be <= n in hypergeometric distribution."); 
    656     enforce(n <= n1 + n2, "n must be <= n1 + n2 in hypergeometric distribution."); 
    657     enforce(x >= 0, "x must be >= 0 in hypergeometric distribution."); 
     655    dstatsEnforce(x <= n, "x must be <= n in hypergeometric distribution."); 
     656    dstatsEnforce(n <= n1 + n2, "n must be <= n1 + n2 in hypergeometric distribution."); 
     657    dstatsEnforce(x >= 0, "x must be >= 0 in hypergeometric distribution."); 
    658658 
    659659    ulong expec = (n1 * n) / (n1 + n2); 
     
    746746///P(X >= x), where X is random variable. 
    747747double hypergeometricCDFR(ulong x, ulong n1, ulong n2, ulong n) { 
    748     enforce(x <= n, "x must be <= n in hypergeometric distribution."); 
    749     enforce(n <= n1 + n2, "n must be <= n1 + n2 in hypergeometric distribution."); 
    750     enforce(x >= 0, "x must be >= 0 in hypergeometric distribution."); 
     748    dstatsEnforce(x <= n, "x must be <= n in hypergeometric distribution."); 
     749    dstatsEnforce(n <= n1 + n2, "n must be <= n1 + n2 in hypergeometric distribution."); 
     750    dstatsEnforce(x >= 0, "x must be >= 0 in hypergeometric distribution."); 
    751751 
    752752    return hypergeometricCDF(n - x, n2, n1, n); 
     
    767767 
    768768double hyperExact(ulong x, ulong n1, ulong n2, ulong n, ulong startAt = 0) { 
    769     enforce(x <= n, "x must be <= n in hypergeometric distribution."); 
    770     enforce(n <= n1 + n2, "n must be <= n1 + n2 in hypergeometric distribution."); 
    771     enforce(x >= 0, "x must be >= 0 in hypergeometric distribution."); 
     769    dstatsEnforce(x <= n, "x must be <= n in hypergeometric distribution."); 
     770    dstatsEnforce(n <= n1 + n2, "n must be <= n1 + n2 in hypergeometric distribution."); 
     771    dstatsEnforce(x >= 0, "x must be >= 0 in hypergeometric distribution."); 
    772772 
    773773    immutable double constPart = logFactorial(n1) + logFactorial(n2) + 
     
    800800/// 
    801801double chiSquarePDF(double x, double v) { 
    802     enforce(x >= 0, "x must be >= 0 in chi-square distribution."); 
    803     enforce(v >= 1.0, "Must have at least 1 degree of freedom for chi-square."); 
     802    dstatsEnforce(x >= 0, "x must be >= 0 in chi-square distribution."); 
     803    dstatsEnforce(v >= 1.0, "Must have at least 1 degree of freedom for chi-square."); 
    804804 
    805805    // Calculate in log space for stability. 
     
    839839 */ 
    840840double chiSquareCDF(double x, double v) { 
    841     enforce(x >= 0, "x must be >= 0 in chi-square distribution."); 
    842     enforce(v >= 1.0, "Must have at least 1 degree of freedom for chi-square."); 
     841    dstatsEnforce(x >= 0, "x must be >= 0 in chi-square distribution."); 
     842    dstatsEnforce(v >= 1.0, "Must have at least 1 degree of freedom for chi-square."); 
    843843    return gammaIncomplete( 0.5*v, 0.5*x); 
    844844} 
     
    846846/// 
    847847double chiSquareCDFR(double x, double v) { 
    848     enforce(x >= 0, "x must be >= 0 in chi-square distribution."); 
    849     enforce(v >= 1.0, "Must have at least 1 degree of freedom for chi-square."); 
     848    dstatsEnforce(x >= 0, "x must be >= 0 in chi-square distribution."); 
     849    dstatsEnforce(v >= 1.0, "Must have at least 1 degree of freedom for chi-square."); 
    850850    return gammaIncompleteCompl( 0.5L*v, 0.5L*x ); 
    851851} 
     
    864864 */ 
    865865double invChiSquareCDFR(double v, double p) { 
    866     enforce(v >= 1.0, "Must have at least 1 degree of freedom for chi-square."); 
    867     enforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
     866    dstatsEnforce(v >= 1.0, "Must have at least 1 degree of freedom for chi-square."); 
     867    dstatsEnforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
    868868    return  2.0 * gammaIncompleteComplInv( 0.5*v, p); 
    869869} 
     
    878878/// 
    879879double normalPDF(double x, double mean = 0, double sd = 1) { 
    880     enforce(sd > 0, "Standard deviation must be > 0 for normal distribution."); 
     880    dstatsEnforce(sd > 0, "Standard deviation must be > 0 for normal distribution."); 
    881881    double dev = x - mean; 
    882882    return exp(-(dev * dev) / (2 * sd * sd)) / (sd * SQ2PI); 
     
    889889///P(X < x) for normal distribution where X is random var. 
    890890double normalCDF(double x, double mean = 0, double stdev = 1) { 
    891     enforce(stdev > 0, "Standard deviation must be > 0 for normal distribution."); 
     891    dstatsEnforce(stdev > 0, "Standard deviation must be > 0 for normal distribution."); 
    892892 
    893893    // Using a slightly non-obvious implementation in terms of erfc because 
     
    907907///P(X > x) for normal distribution where X is random var. 
    908908double normalCDFR(double x, double mean = 0, double stdev = 1) { 
    909     enforce(stdev > 0, "Standard deviation must be > 0 for normal distribution."); 
     909    dstatsEnforce(stdev > 0, "Standard deviation must be > 0 for normal distribution."); 
    910910 
    911911    double Z = (x - mean) / stdev; 
     
    10271027 */ 
    10281028double invNormalCDF(double p, double mean = 0, double sd = 1) { 
    1029     enforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
    1030     enforce(sd > 0, "Standard deviation must be > 0 for normal distribution."); 
     1029    dstatsEnforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
     1030    dstatsEnforce(sd > 0, "Standard deviation must be > 0 for normal distribution."); 
    10311031 
    10321032    if (p == 0.0L) { 
     
    11051105/// 
    11061106double logNormalPDF(double x, double mu = 0, double sigma = 1) { 
    1107     enforce(sigma > 0, "sigma must be > 0 for log-normal distribution."); 
     1107    dstatsEnforce(sigma > 0, "sigma must be > 0 for log-normal distribution."); 
    11081108 
    11091109    immutable mulTerm = 1.0L / (x * sigma * SQ2PI); 
     
    11221122/// 
    11231123double logNormalCDF(double x, double mu = 0, double sigma = 1) { 
    1124     enforce(sigma > 0, "sigma must be > 0 for log-normal distribution."); 
     1124    dstatsEnforce(sigma > 0, "sigma must be > 0 for log-normal distribution."); 
    11251125 
    11261126    return 0.5L + 0.5L * erf((log(x) - mu) / (sigma * SQRT2)); 
     
    11341134/// 
    11351135double logNormalCDFR(double x, double mu = 0, double sigma = 1) { 
    1136     enforce(sigma > 0, "sigma must be > 0 for log-normal distribution."); 
     1136    dstatsEnforce(sigma > 0, "sigma must be > 0 for log-normal distribution."); 
    11371137 
    11381138    return 0.5L - 0.5L * erf((log(x) - mu) / (sigma * SQRT2)); 
     
    11471147/// 
    11481148double weibullPDF(double x, double shape, double scale = 1) { 
    1149     enforce(shape > 0, "shape must be > 0 for weibull distribution."); 
    1150     enforce(scale > 0, "scale must be > 0 for weibull distribution."); 
     1149    dstatsEnforce(shape > 0, "shape must be > 0 for weibull distribution."); 
     1150    dstatsEnforce(scale > 0, "scale must be > 0 for weibull distribution."); 
    11511151 
    11521152    if(x < 0) { 
     
    11631163/// 
    11641164double weibullCDF(double x, double shape, double scale = 1) { 
    1165     enforce(shape > 0, "shape must be > 0 for weibull distribution."); 
    1166     enforce(scale > 0, "scale must be > 0 for weibull distribution."); 
     1165    dstatsEnforce(shape > 0, "shape must be > 0 for weibull distribution."); 
     1166    dstatsEnforce(scale > 0, "scale must be > 0 for weibull distribution."); 
    11671167 
    11681168    double exponent = pow(x / scale, shape); 
     
    11761176/// 
    11771177double weibullCDFR(double x, double shape, double scale = 1) { 
    1178     enforce(shape > 0, "shape must be > 0 for weibull distribution."); 
    1179     enforce(scale > 0, "scale must be > 0 for weibull distribution."); 
     1178    dstatsEnforce(shape > 0, "shape must be > 0 for weibull distribution."); 
     1179    dstatsEnforce(scale > 0, "scale must be > 0 for weibull distribution."); 
    11801180 
    11811181    double exponent = pow(x / scale, shape); 
     
    12041204/// 
    12051205double studentsTCDF(double t, double df) { 
    1206     enforce(df > 0, "Student's T must have >0 degrees of freedom."); 
     1206    dstatsEnforce(df > 0, "Student's T must have >0 degrees of freedom."); 
    12071207 
    12081208    double x = (t + sqrt(t * t + df)) / (2 * sqrt(t * t + df)); 
     
    12371237*/ 
    12381238double invStudentsTCDF(double p, double df) { 
    1239     enforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
    1240     enforce(df > 0, "Student's T must have >0 degrees of freedom."); 
     1239    dstatsEnforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
     1240    dstatsEnforce(df > 0, "Student's T must have >0 degrees of freedom."); 
    12411241 
    12421242    if (p==0) return -double.infinity; 
     
    13021302 */ 
    13031303double fisherCDF(double x, double df1, double df2) { 
    1304     enforce(df1 > 0 && df2 > 0, 
     1304    dstatsEnforce(df1 > 0 && df2 > 0, 
    13051305        "Fisher distribution must have >0 degrees of freedom."); 
    1306     enforce(x >= 0, "x must be >=0 for Fisher distribution."); 
     1306    dstatsEnforce(x >= 0, "x must be >=0 for Fisher distribution."); 
    13071307 
    13081308    double a = cast(double)(df1); 
     
    13151315/** ditto */ 
    13161316double fisherCDFR(double x, double df1, double df2) { 
    1317     enforce(df1 > 0 && df2 > 0, 
     1317    dstatsEnforce(df1 > 0 && df2 > 0, 
    13181318        "Fisher distribution must have >0 degrees of freedom."); 
    1319     enforce(x >= 0, "x must be >=0 for Fisher distribution."); 
     1319    dstatsEnforce(x >= 0, "x must be >=0 for Fisher distribution."); 
    13201320 
    13211321    double a = cast(double)(df1); 
     
    13451345*/ 
    13461346double invFisherCDFR(double df1, double df2, double p ) { 
    1347     enforce(df1 > 0 && df2 > 0, 
     1347    dstatsEnforce(df1 > 0 && df2 > 0, 
    13481348        "Fisher distribution must have >0 degrees of freedom."); 
    1349     enforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
     1349    dstatsEnforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
    13501350 
    13511351    double a = df1; 
     
    13771377/// 
    13781378double negBinomPMF(ulong k, ulong n, double p) { 
    1379     enforce(p >= 0 && p <= 1, 
     1379    dstatsEnforce(p >= 0 && p <= 1, 
    13801380        "p must be between 0, 1 for negative binomial distribution."); 
    13811381 
     
    14141414 */ 
    14151415double negBinomCDF(ulong k, ulong n, double p ) { 
    1416     enforce(p >= 0 && p <= 1, 
     1416    dstatsEnforce(p >= 0 && p <= 1, 
    14171417        "p must be between 0, 1 for negative binomial distribution."); 
    14181418    if ( k == 0 ) return pow(p, cast(double) n); 
     
    14281428/**Probability that k or more failures precede the nth success.*/ 
    14291429double negBinomCDFR(ulong k, ulong n, double p) { 
    1430     enforce(p >= 0 && p <= 1, 
     1430    dstatsEnforce(p >= 0 && p <= 1, 
    14311431        "p must be between 0, 1 for negative binomial distribution."); 
    14321432 
     
    14431443/// 
    14441444ulong invNegBinomCDF(double pVal, ulong n, double p) { 
    1445     enforce(p >= 0 && p <= 1, 
     1445    dstatsEnforce(p >= 0 && p <= 1, 
    14461446        "p must be between 0, 1 for negative binomial distribution."); 
    1447     enforce(pVal >= 0 && pVal <= 1, 
     1447    dstatsEnforce(pVal >= 0 && pVal <= 1, 
    14481448        "P-values must be between 0, 1."); 
    14491449 
     
    15401540 */ 
    15411541double gammaCDF(double x, double a, double b) { 
    1542     enforce(x > 0, "x must be >0 in gamma distribution."); 
    1543     enforce(a > 0, "a must be >0 in gamma distribution."); 
    1544     enforce(b > 0, "b must be >0 in gamma distribution."); 
     1542    dstatsEnforce(x > 0, "x must be >0 in gamma distribution."); 
     1543    dstatsEnforce(a > 0, "a must be >0 in gamma distribution."); 
     1544    dstatsEnforce(b > 0, "b must be >0 in gamma distribution."); 
    15451545 
    15461546    return gammaIncomplete(b, a*x); 
     
    15491549/** ditto */ 
    15501550double gammaCDFR(double x, double a, double b) { 
    1551     enforce(x > 0, "x must be >0 in gamma distribution."); 
    1552     enforce(a > 0, "a must be >0 in gamma distribution."); 
    1553     enforce(b > 0, "b must be >0 in gamma distribution."); 
     1551    dstatsEnforce(x > 0, "x must be >0 in gamma distribution."); 
     1552    dstatsEnforce(a > 0, "a must be >0 in gamma distribution."); 
     1553    dstatsEnforce(b > 0, "b must be >0 in gamma distribution."); 
    15541554 
    15551555    return gammaIncompleteCompl( b, a * x ); 
     
    15581558/// 
    15591559double invGammaCDFR(double p, double a, double b) { 
    1560     enforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
    1561     enforce(a > 0, "a must be >0 in gamma distribution."); 
    1562     enforce(b > 0, "b must be >0 in gamma distribution."); 
     1560    dstatsEnforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
     1561    dstatsEnforce(a > 0, "a must be >0 in gamma distribution."); 
     1562    dstatsEnforce(b > 0, "b must be >0 in gamma distribution."); 
    15631563 
    15641564    double ax = gammaIncompleteComplInv(b, p); 
     
    15731573/// 
    15741574double cauchyPDF(double X, double X0 = 0, double gamma = 1) { 
    1575     enforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
     1575    dstatsEnforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
    15761576 
    15771577    double toSquare = (X - X0) / gamma; 
     
    15881588/// 
    15891589double cauchyCDF(double X, double X0 = 0, double gamma = 1) { 
    1590     enforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
     1590    dstatsEnforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
    15911591 
    15921592    return M_1_PI * atan((X - X0) / gamma) + 0.5L; 
     
    16011601/// 
    16021602double cauchyCDFR(double X, double X0 = 0, double gamma = 1) { 
    1603     enforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
     1603    dstatsEnforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
    16041604 
    16051605    return M_1_PI * atan((X0 - X) / gamma) + 0.5L; 
     
    16151615/// 
    16161616double invCauchyCDF(double p, double X0 = 0, double gamma = 1) { 
    1617     enforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
    1618     enforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
     1617    dstatsEnforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
     1618    dstatsEnforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
    16191619 
    16201620    return X0 + gamma * tan(PI * (p - 0.5L)); 
     
    16371637/// 
    16381638double laplacePDF(double x, double mu = 0, double b = 1) { 
    1639     enforce(b > 0, "b must be > 0 for laplace distribution."); 
     1639    dstatsEnforce(b > 0, "b must be > 0 for laplace distribution."); 
    16401640 
    16411641    return (exp(-abs(x - mu) / b)) / (2 * b); 
     
    16501650/// 
    16511651double laplaceCDF(double X, double mu = 0, double b = 1) { 
    1652     enforce(b > 0, "b must be > 0 for laplace distribution."); 
     1652    dstatsEnforce(b > 0, "b must be > 0 for laplace distribution."); 
    16531653 
    16541654    double diff = (X - mu); 
     
    16661666/// 
    16671667double laplaceCDFR(double X, double mu = 0, double b = 1) { 
    1668     enforce(b > 0, "b must be > 0 for laplace distribution."); 
     1668    dstatsEnforce(b > 0, "b must be > 0 for laplace distribution."); 
    16691669 
    16701670    double diff = (mu - X); 
     
    16831683/// 
    16841684double invLaplaceCDF(double p, double mu = 0, double b = 1) { 
    1685     enforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
    1686     enforce(b > 0, "b must be > 0 for laplace distribution."); 
     1685    dstatsEnforce(p >= 0 && p <= 1, "P-values must be between 0, 1."); 
     1686    dstatsEnforce(b > 0, "b must be > 0 for laplace distribution."); 
    16871687 
    16881688    double p05 = p - 0.5L; 
     
    17001700/**Kolmogorov distribution.  Used in Kolmogorov-Smirnov testing.*/ 
    17011701double kolmDist(double X) { 
    1702     enforce(X >= 0, "X must be >= 0 for Kolmogorov distribution."); 
     1702    dstatsEnforce(X >= 0, "X must be >= 0 for Kolmogorov distribution."); 
    17031703 
    17041704    if(X == 0) { 
  • trunk/infotheory.d

    r159 r164  
    5757double entropyCounts(T)(T data) 
    5858if(isForwardRange!(T) && doubleInput!(T)) { 
    59     auto save = data
     59    auto save = data.save()
    6060    return entropyCounts(save, sum!(T, double)(data)); 
    6161} 
     
    262262    } else static if(isArray!(T)) { 
    263263        enum bool NeedsHeap = false; 
    264     } else static if(is(T == Joint!(typeof(T.init.tupleof))) 
     264    } else static if(is(Joint!(typeof(T.init.tupleof))) 
     265           && is(T == Joint!(typeof(T.init.tupleof))) 
    265266           && allSatisfy!(isArray, typeof(T.init.tupleof))) { 
    266267        enum bool NeedsHeap = false; 
     
    272273unittest { 
    273274    auto foo = map!("a.dup")(cast(uint[][]) [[1]]); 
    274     auto bar = map!("a + 2")(cast(uint[]) [1,2,3]); 
     275    auto bar = map!("a + 2")([1,2,3][]); 
    275276    static assert(NeedsHeap!(typeof(foo))); 
    276277    static assert(!NeedsHeap!(typeof(bar))); 
  • trunk/random.d

    r159 r164  
    122122public import std.random; //For uniform distrib. 
    123123 
    124 import dstats.alloc
     124import dstats.alloc, dstats.base
    125125 
    126126version(unittest) { 
     
    200200        normData = *lastNormPtr; 
    201201        *lastNormPtr = temp; 
     202    } 
     203 
     204    typeof(this) save() @property { 
     205        return this; 
     206    } 
     207 
     208    // Doesn't do anything particularly useful.  It's more a workaround for 
     209    // bug 4305. 
     210    typeof(randFun(args)) moveFront() { 
     211        return frontElem; 
    202212    } 
    203213} 
     
    263273/// 
    264274double rNorm(RGen = Random)(double mean, double sd, ref RGen gen = rndGen) { 
    265     enforce(sd > 0, "Standard deviation must be > 0 for rNorm."); 
     275    dstatsEnforce(sd > 0, "Standard deviation must be > 0 for rNorm."); 
    266276 
    267277    double lr = lastNorm; 
     
    298308/// 
    299309double rCauchy(RGen = Random)(double X0, double gamma, ref RGen gen = rndGen) { 
    300     enforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
     310    dstatsEnforce(gamma > 0, "gamma must be > 0 for Cauchy distribution."); 
    301311 
    302312    return (rNorm(0, 1, gen) / rNorm(0, 1, gen)) * gamma + X0; 
     
    318328/// 
    319329double rStudentT(RGen = Random)(double df, ref RGen gen = rndGen) { 
    320     enforce(df > 0, "Student's T distribution must have >0 degrees of freedom."); 
     330    dstatsEnforce(df > 0, "Student's T distribution must have >0 degrees of freedom."); 
    321331 
    322332    double N = rNorm(0, 1, gen); 
     
    341351/// 
    342352double rFisher(RGen = Random)(double df1, double df2, ref RGen gen = rndGen) { 
    343     enforce(df1 > 0 && df2 > 0, 
     353    dstatsEnforce(df1 > 0 && df2 > 0, 
    344354        "df1 and df2 must be >0 for the Fisher distribution."); 
    345355 
     
    362372/// 
    363373double rChiSquare(RGen = Random)(double df, ref RGen gen = rndGen) { 
    364     enforce(df > 0, "df must be > 0 for chiSquare distribution."); 
     374    dstatsEnforce(df > 0, "df must be > 0 for chiSquare distribution."); 
    365375 
    366376    return 2.0 * stdGamma(df / 2.0L, gen); 
     
    384394/// 
    385395int rPoisson(RGen = Random)(double lam, ref RGen gen = rndGen) { 
    386     enforce(lam > 0, "lambda must be >0 for Poisson distribution."); 
     396    dstatsEnforce(lam > 0, "lambda must be >0 for Poisson distribution."); 
    387397 
    388398    static int poissonMult(ref RGen gen, double lam) { 
     
    466476/// 
    467477int rBernoulli(RGen = Random)(double P = 0.5, ref RGen gen = rndGen) { 
    468     enforce(P >= 0 && P <= 1, "P must be between 0, 1 for Bernoulli distribution."); 
     478    dstatsEnforce(P >= 0 && P <= 1, "P must be between 0, 1 for Bernoulli distribution."); 
    469479 
    470480    double pVal = uniform(0.0L, 1.0L, gen); 
     
    662672/// 
    663673int rBinomial(RGen = Random)(int n, double p, ref RGen gen = rndGen) { 
    664     enforce(n >= 0, "n must be >= 0 for binomial distribution."); 
    665     enforce(p >= 0 && p <= 1, "p must be between 0, 1 for binomial distribution."); 
     674    dstatsEnforce(n >= 0, "n must be >= 0 for binomial distribution."); 
     675    dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 for binomial distribution."); 
    666676 
    667677    if (p <= 0.5) { 
     
    776786/// 
    777787int rHypergeometric(RGen = Random)(int n1, int n2, int n, ref RGen gen = rndGen) { 
    778     enforce(n <= n1 + n2, "n must be <= n1 + n2 for hypergeometric distribution."); 
    779     enforce(n1 >= 0 && n2 >= 0 && n >= 0, 
     788    dstatsEnforce(n <= n1 + n2, "n must be <= n1 + n2 for hypergeometric distribution."); 
     789    dstatsEnforce(n1 >= 0 && n2 >= 0 && n >= 0, 
    780790        "n, n1, n2 must be >= 0 for hypergeometric distribution."); 
    781791 
     
    843853 
    844854int rGeometric(RGen = Random)(double p, ref RGen gen = rndGen) { 
    845     enforce(p >= 0 && p <= 1, "p must be between 0, 1 for geometric distribution."); 
     855    dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 for geometric distribution."); 
    846856 
    847857    if (p >= 0.333333333333333333333333) { 
     
    879889/// 
    880890int rNegBinom(RGen = Random)(double n, double p, ref RGen gen = rndGen) { 
    881     enforce(n >= 0, "n must be >= 0 for negative binomial distribution."); 
    882     enforce(p >= 0 && p <= 1, 
     891    dstatsEnforce(n >= 0, "n must be >= 0 for negative binomial distribution."); 
     892    dstatsEnforce(p >= 0 && p <= 1, 
    883893        "p must be between 0, 1 for negative binomial distribution."); 
    884894 
     
    911921/// 
    912922double rLaplace(RGen = Random)(double mu = 0, double b = 1, ref RGen gen = rndGen) { 
    913     enforce(b > 0, "b must be > 0 for Laplace distribution."); 
     923    dstatsEnforce(b > 0, "b must be > 0 for Laplace distribution."); 
    914924 
    915925    double p = uniform(0.0L, 1.0L, gen); 
     
    935945/// 
    936946double rExponential(RGen = Random)(double lambda, ref RGen gen = rndGen) { 
    937     enforce(lambda > 0, "lambda must be > 0 for exponential distribution."); 
     947    dstatsEnforce(lambda > 0, "lambda must be > 0 for exponential distribution."); 
    938948 
    939949    double p = uniform(0.0L, 1.0L, gen); 
     
    9971007/// 
    9981008double rGamma(RGen = Random)(double a, double b, ref RGen gen = rndGen) { 
    999     enforce(a > 0, "a must be > 0 for gamma distribution."); 
    1000     enforce(b > 0, "b must be > 0 for gamma distribution."); 
     1009    dstatsEnforce(a > 0, "a must be > 0 for gamma distribution."); 
     1010    dstatsEnforce(b > 0, "b must be > 0 for gamma distribution."); 
    10011011 
    10021012    return stdGamma(b, gen) / a; 
     
    10191029/// 
    10201030double rBeta(RGen = Random)(double a, double b, ref RGen gen = rndGen) { 
    1021     enforce(a > 0, "a must be > 0 for beta distribution."); 
    1022     enforce(b > 0, "b must be > 0 for beta distribution."); 
     1031    dstatsEnforce(a > 0, "a must be > 0 for beta distribution."); 
     1032    dstatsEnforce(b > 0, "b must be > 0 for beta distribution."); 
    10231033 
    10241034    double Ga = void, Gb = void; 
     
    10911101/// 
    10921102double rLogistic(RGen = Random)(double loc, double scale, ref RGen gen = rndGen) { 
    1093     enforce(scale > 0, "scale must be > 0 for logistic distribution."); 
     1103    dstatsEnforce(scale > 0, "scale must be > 0 for logistic distribution."); 
    10941104 
    10951105    double U = uniform(0.0L, 1.0L, gen); 
     
    11131123/// 
    11141124double rLogNorm(RGen = Random)(double mu, double sigma, ref RGen gen = rndGen) { 
    1115     enforce(sigma > 0, "sigma must be > 0 for log-normal distribution."); 
     1125    dstatsEnforce(sigma > 0, "sigma must be > 0 for log-normal distribution."); 
    11161126 
    11171127    return exp(rNorm(mu, sigma, gen)); 
     
    11351145/// 
    11361146double rWeibull(RGen = Random)(double shape, double scale = 1, ref RGen gen = rndGen) { 
    1137     enforce(shape > 0, "shape must be > 0 for weibull distribution."); 
    1138     enforce(scale > 0, "scale must be > 0 for weibull distribution."); 
     1147    dstatsEnforce(shape > 0, "shape must be > 0 for weibull distribution."); 
     1148    dstatsEnforce(scale > 0, "scale must be > 0 for weibull distribution."); 
    11391149 
    11401150    return pow(rExponential(1, gen), 1. / shape) * scale; 
     
    11521162/// 
    11531163double rWald(RGen = Random)(double mu, double lambda, ref RGen gen = rndGen) { 
    1154     enforce(mu > 0, "mu must be > 0 for Wald distribution."); 
    1155     enforce(lambda > 0, "lambda must be > 0 for Wald distribution."); 
     1164    dstatsEnforce(mu > 0, "mu must be > 0 for Wald distribution."); 
     1165    dstatsEnforce(lambda > 0, "lambda must be > 0 for Wald distribution."); 
    11561166 
    11571167    alias mu mean; 
     
    11871197/// 
    11881198double rRayleigh(RGen = Random)(double mode, ref RGen gen = rndGen) { 
    1189     enforce(mode > 0, "mode must be > 0 for Rayleigh distribution."); 
     1199    dstatsEnforce(mode > 0, "mode must be > 0 for Rayleigh distribution."); 
    11901200 
    11911201    return mode*sqrt(-2.0 * log(1.0 - uniform(0.0L, 1.0L, gen))); 
  • trunk/regress.d

    r145 r164  
    3636 
    3737import std.math, std.algorithm, std.traits, std.array, std.traits, std.contracts, 
    38     dstats.alloc, std.range, std.conv, dstats.distrib, dstats.cor, dstats.base; 
     38    std.typetuple; 
     39 
     40import dstats.alloc, std.range, std.conv, dstats.distrib, dstats.cor, dstats.base; 
    3941 
    4042private void enforceConfidence(double conf) { 
    41     enforce(conf >= 0 && conf <= 1, 
     43    dstatsEnforce(conf >= 0 && conf <= 1, 
    4244        "Confidence intervals must be between 0 and 1."); 
    4345} 
     
    7274    } 
    7375 
    74     bool empty() { 
     76    typeof(this) save() @property { 
     77        return this; 
     78    } 
     79 
     80    bool empty() @property { 
    7581        return range.empty; 
    7682    } 
     
    269275        size_t i = 0; 
    270276        foreach(elem; X) { 
    271             sum += cast(double) elem.front * betas[i]; 
     277            double frnt = elem.front(); 
     278            sum += frnt * betas[i]; 
    272279            i++; 
    273280        } 
     
    277284    this(F[] betas, U Y, R X) { 
    278285        static if(is(typeof(X.length))) { 
    279             enforce(X.length == betas.length, 
     286            dstatsEnforce(X.length == betas.length, 
    280287                "Betas and X must have same length for residuals."); 
    281288        } else { 
    282             enforce(walkLength(X) == betas.length, 
     289            dstatsEnforce(walkLength(X) == betas.length, 
    283290                "Betas and X must have same length for residuals."); 
    284291        } 
     
    434441    static if(isForwardRange!(T[0]) && isForwardRange!(typeof(XIn[0].front())) && 
    435442        T.length == 1) { 
     443 
     444        enum bool arrayX = true; 
    436445        alias typeof(XIn[0].front) E; 
    437446        E[] X = tempdup(XIn[0]); 
    438447    } else static if(allSatisfy!(isForwardRange, T)) { 
     448        enum bool arrayX = false; 
    439449        alias XIn X; 
    440450    } else { 
     
    445455    double[][] xTx; 
    446456    double[] xTy; 
    447     rangeMatrixMulTrans(xTy, xTx, Y, X); 
     457 
     458    typeof(X) xSaved; 
     459    static if(arrayX) { 
     460        xSaved = X.tempdup; 
     461        foreach(ref elem; xSaved) { 
     462            elem = elem.save; 
     463        } 
     464    } else { 
     465        xSaved = saveAll(X).expand; 
     466    } 
     467 
     468    rangeMatrixMulTrans(xTy, xTx, Y.save, X); 
    448469    invert(xTx); 
    449470    double[] betas = new double[X.length]; 
  • trunk/tests.d

    r159 r164  
    3232 
    3333import std.algorithm, std.functional, std.range, std.conv, std.math, std.traits, 
    34        std.contracts
     34       std.contracts, std.typetuple
    3535 
    3636import dstats.base, dstats.distrib, dstats.alloc, dstats.summary, dstats.sort, 
     
    4343 
    4444private void enforceConfidence(double conf) { 
    45     enforce(conf >= 0 && conf <= 1, 
     45    dstatsEnforce(conf >= 0 && conf <= 1, 
    4646        "Confidence intervals must be between 0 and 1."); 
    4747} 
     
    149149if( (isSummary!T || doubleIterable!T)) { 
    150150    enforceConfidence(confLevel); 
    151     enforce(isFinite(testMean), "testMean must not be infinite or nan."); 
     151    dstatsEnforce(isFinite(testMean), "testMean must not be infinite or nan."); 
    152152 
    153153    static if(isSummary!T) { 
     
    197197if( (doubleIterable!T || isSummary!T) && (doubleIterable!U || isSummary!U)) { 
    198198    enforceConfidence(confLevel); 
    199     enforce(isFinite(testMean), "testMean must not be infinite or nan."); 
     199    dstatsEnforce(isFinite(testMean), "testMean must not be infinite or nan."); 
    200200 
    201201    static if(isSummary!T) { 
     
    311311if( (isSummary!T || doubleIterable!T) && (isSummary!U || doubleIterable!U)) { 
    312312    enforceConfidence(confLevel); 
    313     enforce(isFinite(testMean), "testMean cannot be infinite or nan."); 
     313    dstatsEnforce(isFinite(testMean), "testMean cannot be infinite or nan."); 
    314314 
    315315    static if(isSummary!T) { 
     
    428428if(doubleInput!(T) && doubleInput!(U) && isInputRange!T && isInputRange!U) { 
    429429    enforceConfidence(confLevel); 
    430     enforce(isFinite(testMean), "testMean cannot be infinite or nan."); 
     430    dstatsEnforce(isFinite(testMean), "testMean cannot be infinite or nan."); 
    431431 
    432432    MeanSD msd; 
     
    466466if(isSummary!T) { 
    467467    enforceConfidence(confLevel); 
    468     enforce(isFinite(testMean), "testMean cannot be infinite or nan."); 
     468    dstatsEnforce(isFinite(testMean), "testMean cannot be infinite or nan."); 
    469469 
    470470    // Save typing. 
     
    640640    auto res2 = fTest([thing1, thing2, thing3].dup); 
    641641    assert(approxEqual(result.testStat, res2.testStat)); 
    642     assert(result.p == res2.p); 
     642    assert(approxEqual(result.p, res2.p)); 
    643643 
    644644    thing1 = [2,7,1,8,2]; 
     
    692692            // The cast is to force conversions to double on alias this'd stuff 
    693693            // like the Mean struct. 
    694             centers[i] = cast(double) central(category); 
     694            centers[i] = cast(double) central(category.save); 
    695695        } 
    696696 
     
    14811481 
    14821482    static if(dstats.base.hasLength!T && dstats.base.hasLength!U) { 
    1483         enforce(before.length == after.length, 
     1483        dstatsEnforce(before.length == after.length, 
    14841484            "Ranges must have same lengths for wilcoxonSignedRank."); 
    14851485 
     
    18271827 */ 
    18281828double binomialTest(ulong k, ulong n, double p) { 
    1829     enforce(k <= n, "k must be <= n for binomial test."); 
    1830     enforce(p >= 0 && p <= 1, "p must be between 0, 1 for binomial test."); 
     1829    dstatsEnforce(k <= n, "k must be <= n for binomial test."); 
     1830    dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 for binomial test."); 
    18311831 
    18321832    enum epsilon = 1 - 1e-6;  // Small but arbitrary constant to deal w/ rounding error. 
     
    20132013if(doubleInput!(T) && doubleInput!(U)) { 
    20142014    if(countProp == Expected.PROPORTION) { 
    2015         enforce(isForwardRange!(U), 
     2015        dstatsEnforce(isForwardRange!(U), 
    20162016            "Can't use expected proportions instead of counts with input ranges."); 
    20172017    } 
     
    20252025        double expectSum = 0; 
    20262026        multiplier = 0; 
    2027         auto obsCopy = observed
    2028         auto expCopy = expected
     2027        auto obsCopy = observed.save
     2028        auto expCopy = expected.save
    20292029        while(!obsCopy.empty && !expCopy.empty) { 
    20302030            multiplier += obsCopy.front; 
     
    20462046        // exist. 
    20472047        if(e == 0) { 
    2048             enforce(observed.front == 0, 
     2048            dstatsEnforce(observed.front == 0, 
    20492049                "Can't have non-zero observed value w/ zero expected value."); 
    20502050            continue; 
     
    22972297    foreach(ri, range; ranges) { 
    22982298        size_t curLen = 0; 
    2299         foreach(elem; range) { 
     2299        foreach(elem; range.save) { 
    23002300            colSums[ri] += cast(double) elem; 
    23012301            curLen++; 
     
    23892389    foreach(range; contingencyTable) { 
    23902390        foreach(elem; range) { 
    2391             enforce(elem >= 0, 
     2391            dstatsEnforce(elem >= 0, 
    23922392                "Cannot have negative elements in a contingency table."); 
    23932393        } 
     
    24072407 
    24082408 
    2409     alias contingencyTable c; 
     2409    alias contingencyTable c;  // Save typing. 
    24102410    immutable oddsRatio = cast(double) c[0][0] * c[1][1] / c[0][1] / c[1][0]; 
    24112411    if(alt == Alt.NONE) { 
     
    25142514TestRes fisherExact(T)(const T[][] contingencyTable, Alt alt = Alt.TWOSIDE) 
    25152515if(isIntegral!(T)) { 
    2516     enforce(contingencyTable.length == 2 && 
     2516    dstatsEnforce(contingencyTable.length == 2 && 
    25172517            contingencyTable[0].length == 2 && 
    25182518            contingencyTable[1].length == 2, 
     
    28882888ConfInt pearsonCorTest(T)(double cor, T N, Alt alt = Alt.TWOSIDE, double confLevel = 0.95) 
    28892889if(isNumeric!(T)) { 
    2890     enforce(N >= 0, "N must be >= 0 for pearsonCorTest."); 
    2891     enforce(cor > -1.0 || approxEqual(cor, -1.0), 
     2890    dstatsEnforce(N >= 0, "N must be >= 0 for pearsonCorTest."); 
     2891    dstatsEnforce(cor > -1.0 || approxEqual(cor, -1.0), 
    28922892        "Correlation must be between 0, 1."); 
    2893     enforce(cor < 1.0 || approxEqual(cor, 1.0), 
     2893    dstatsEnforce(cor < 1.0 || approxEqual(cor, 1.0), 
    28942894         "Correlation must be between 0, 1."); 
    28952895    enforceConfidence(confLevel); 
     
    33673367    uint df = 0; 
    33683368    foreach(pVal; pVals) { 
    3369         enforce(pVal >= 0 && pVal <= 1, 
     3369        dstatsEnforce(pVal >= 0 && pVal <= 1, 
    33703370            "P-values must be between 0, 1 for Fisher's Method."); 
    33713371        chiSq += log( cast(double) pVal); 
     
    34223422        qVals.length = pVals.length; 
    34233423        foreach(i, elem; pVals) { 
    3424             enforce(elem >= 0 && elem <= 1, 
     3424            dstatsEnforce(elem >= 0 && elem <= 1, 
    34253425                "P-values must be between 0, 1 for falseDiscoveryRate."); 
    34263426            qVals[i] = cast(float) elem; 
     
    35343534 
    35353535    foreach(i, ref q; qVals) { 
    3536         enforce(q >= 0 && q <= 1, 
     3536        dstatsEnforce(q >= 0 && q <= 1, 
    35373537            "P-values must be between 0, 1 for hochberg."); 
    35383538        q = min(1.0f, q * (cast(double) qVals.length - i)); 
     
    36043604 
    36053605    foreach(i, ref q; qVals) { 
    3606         enforce(q >= 0 && q <= 1, 
     3606        dstatsEnforce(q >= 0 && q <= 1, 
    36073607            "P-values must be between 0, 1 for holmBonferroni."); 
    36083608        q = min(1.0, q * (cast(double) qVals.length - i));