“use strict”; module.exports = function(Promise,

PromiseArray,
apiRejection,
tryConvertToPromise,
INTERNAL) {

var getDomain = Promise._getDomain; var async = require(“./async.js”); var util = require(“./util.js”); var tryCatch = util.tryCatch; var errorObj = util.errorObj; function ReductionPromiseArray(promises, fn, accum, _each) {

this.constructor$(promises);
this._promise._captureStackTrace();
this._preservedValues = _each === INTERNAL ? [] : null;
this._zerothIsAccum = (accum === undefined);
this._gotAccum = false;
this._reducingIndex = (this._zerothIsAccum ? 1 : 0);
this._valuesPhase = undefined;
var maybePromise = tryConvertToPromise(accum, this._promise);
var rejected = false;
var isPromise = maybePromise instanceof Promise;
if (isPromise) {
    maybePromise = maybePromise._target();
    if (maybePromise._isPending()) {
        maybePromise._proxyPromiseArray(this, -1);
    } else if (maybePromise._isFulfilled()) {
        accum = maybePromise._value();
        this._gotAccum = true;
    } else {
        this._reject(maybePromise._reason());
        rejected = true;
    }
}
if (!(isPromise || this._zerothIsAccum)) this._gotAccum = true;
var domain = getDomain();
this._callback = domain === null ? fn : domain.bind(fn);
this._accum = accum;
if (!rejected) async.invoke(init, this, undefined);

} function init() {

this._init$(undefined, -5);

} util.inherits(ReductionPromiseArray, PromiseArray);

ReductionPromiseArray.prototype._init = function () {};

ReductionPromiseArray.prototype._resolveEmptyArray = function () {

if (this._gotAccum || this._zerothIsAccum) {
    this._resolve(this._preservedValues !== null
                    ? [] : this._accum);
}

};

ReductionPromiseArray.prototype._promiseFulfilled = function (value, index) {

var values = this._values;
values[index] = value;
var length = this.length();
var preservedValues = this._preservedValues;
var isEach = preservedValues !== null;
var gotAccum = this._gotAccum;
var valuesPhase = this._valuesPhase;
var valuesPhaseIndex;
if (!valuesPhase) {
    valuesPhase = this._valuesPhase = new Array(length);
    for (valuesPhaseIndex=0; valuesPhaseIndex<length; ++valuesPhaseIndex) {
        valuesPhase[valuesPhaseIndex] = 0;
    }
}
valuesPhaseIndex = valuesPhase[index];

if (index === 0 && this._zerothIsAccum) {
    this._accum = value;
    this._gotAccum = gotAccum = true;
    valuesPhase[index] = ((valuesPhaseIndex === 0)
        ? 1 : 2);
} else if (index === -1) {
    this._accum = value;
    this._gotAccum = gotAccum = true;
} else {
    if (valuesPhaseIndex === 0) {
        valuesPhase[index] = 1;
    } else {
        valuesPhase[index] = 2;
        this._accum = value;
    }
}
if (!gotAccum) return;

var callback = this._callback;
var receiver = this._promise._boundValue();
var ret;

for (var i = this._reducingIndex; i < length; ++i) {
    valuesPhaseIndex = valuesPhase[i];
    if (valuesPhaseIndex === 2) {
        this._reducingIndex = i + 1;
        continue;
    }
    if (valuesPhaseIndex !== 1) return;
    value = values[i];
    this._promise._pushContext();
    if (isEach) {
        preservedValues.push(value);
        ret = tryCatch(callback).call(receiver, value, i, length);
    }
    else {
        ret = tryCatch(callback)
            .call(receiver, this._accum, value, i, length);
    }
    this._promise._popContext();

    if (ret === errorObj) return this._reject(ret.e);

    var maybePromise = tryConvertToPromise(ret, this._promise);
    if (maybePromise instanceof Promise) {
        maybePromise = maybePromise._target();
        if (maybePromise._isPending()) {
            valuesPhase[i] = 4;
            return maybePromise._proxyPromiseArray(this, i);
        } else if (maybePromise._isFulfilled()) {
            ret = maybePromise._value();
        } else {
            return this._reject(maybePromise._reason());
        }
    }

    this._reducingIndex = i + 1;
    this._accum = ret;
}

this._resolve(isEach ? preservedValues : this._accum);

};

function reduce(promises, fn, initialValue, _each) {

if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a    See http://goo.gl/916lJJ\u000a");
var array = new ReductionPromiseArray(promises, fn, initialValue, _each);
return array.promise();

}

Promise.prototype.reduce = function (fn, initialValue) {

return reduce(this, fn, initialValue, null);

};

Promise.reduce = function (promises, fn, initialValue, _each) {

return reduce(promises, fn, initialValue, _each);

}; };