
import { twoProduct } from '../basic/two-product.js';
import { twoSum } from '../basic/two-sum.js';
import { fastTwoSum } from '../basic/fast-two-sum.js';
import { eCompress } from './e-compress.js';


const f = 134217729; // 2**27 + 1;


// We *have* to do the below❗ The assignee is a getter❗ The assigned is a pure function❗
const tp = twoProduct;
const ts = twoSum;
const fts = fastTwoSum;
const compress = eCompress;


/**
 * Returns the result of multiplying an expansion by a double.
 * 
 * * see [Shewchuk](https://people.eecs.berkeley.edu/~jrs/papers/robustr.pdf)
 * 
 * Theorem 19 (Shwechuk): Let e = sum_(i=1)^m(e_i) be a nonoverlapping expansion 
 * of m p-bit components, and const b be a p-bit value where p >= 4. Suppose that 
 * the components of e are sorted in order of increasing magnitude, except that
 * any of the e_i may be zero. Then the following algorithm will produce a 
 * nonoverlapping expansion h such that h = sum_(i=1)^(2m)(h_i) = be, where the 
 * components of h are also in order of increasing magnitude, except that any of 
 * the h_i may be zero. Furthermore, if e is nonadjacent and round-to-even 
 * tiebreaking is used, then h is non-adjacent.
 *
 * @param e a double floating point expansion
 * @param b a double
 */
function scaleExpansion(e: number[], b: number): number[] {
    const m = e.length;

    //const h: number[] = new Array(2*m);
    let q_: number;

    //[h[0], q] = tp(e[0], b);
    // inlined (above line)
    const a = e[0];
    let q = a*b;
    const c = f * a; const ah = c - (c - a); const al = a - ah;
    const d = f * b; const bh = d - (d - b); const bl = b - bh;
    const h: number[] = [];
    //h[0] = (al*bl) - ((q - (ah*bh)) - (al*bh) - (ah*bl));
    const hh = (al*bl) - ((q - (ah*bh)) - (al*bh) - (ah*bl));
    if (hh !== 0) { h.push(hh) }

    for (let i=1; i<m; i++) {
        //const [t, T] = tp(e[i], b);
        // inlined (above line)
        const a = e[i];
        const T = a*b;
        const c = f * a; const ah = c - (c - a); const al = a - ah;
        const d = f * b; const bh = d - (d - b); const bl = b - bh;
        const t = (al*bl) - ((T - (ah*bh)) - (al*bh) - (ah*bl));

        //[h[2*i-1], q_] = ts(q, t);
        // inlined (above line)
        const x = q + t;
        const bv = x - q;
        //h[2*i-1] = (q - (x - bv)) + (t - bv);
        //h.push((q - (x - bv)) + (t - bv));
        const hh = (q - (x - bv)) + (t - bv);
        if (hh !== 0) { h.push(hh); }
        q_ = x;

        //[h[2*i], q] = fts(T, q_);
        // inlined (above line)
        const xx = T + q_;
        //h[2*i] = q_ - (xx - T);
        //h.push(q_ - (xx - T));
        const hhh = q_ - (xx - T);
        if (hhh !== 0) { h.push(hhh); }
        q = xx;
    }

    //h[2*m - 1] = q;
    //h.push(q);
    if (q !== 0 || h.length === 0) { h.push(q); }

    //return eCompress(h);
    return h;
}


/**
 * Returns the result of multiplying an expansion by a double.
 * 
 * * see [Shewchuk](https://people.eecs.berkeley.edu/~jrs/papers/robustr.pdf)
 * 
 * Theorem 19 (Shwechuk): Let e = sum_(i=1)^m(e_i) be a nonoverlapping expansion 
 * of m p-bit components, and const b be a p-bit value where p >= 4. Suppose that 
 * the components of e are sorted in order of increasing magnitude, except that
 * any of the e_i may be zero. Then the following algorithm will produce a 
 * nonoverlapping expansion h such that h = sum_(i=1)^(2m)(h_i) = be, where the 
 * components of h are also in order of increasing magnitude, except that any of 
 * the h_i may be zero. Furthermore, if e is nonadjacent and round-to-even 
 * tiebreaking is used, then h is non-adjacent.
 *
 * @param e a double floating point expansion
 * @param b a double
 */
function scaleExpansion2(b: number, e: number[]): number[] {
    const m = e.length;

    //const h: number[] = new Array(2*m);
    let q_: number;

    //[h[0], q] = tp(e[0], b);
    // inlined (above line)
    const a = e[0];
    let q = a*b;
    const c = f * a; const ah = c - (c - a); const al = a - ah;
    const d = f * b; const bh = d - (d - b); const bl = b - bh;
    const h: number[] = [];
    //h[0] = (al*bl) - ((q - (ah*bh)) - (al*bh) - (ah*bl));
    const hh = (al*bl) - ((q - (ah*bh)) - (al*bh) - (ah*bl));
    if (hh !== 0) { h.push(hh) }

    for (let i=1; i<m; i++) {
        //const [t, T] = tp(e[i], b);
        // inlined (above line)
        const a = e[i];
        const T = a*b;
        const c = f * a; const ah = c - (c - a); const al = a - ah;
        const d = f * b; const bh = d - (d - b); const bl = b - bh;
        const t = (al*bl) - ((T - (ah*bh)) - (al*bh) - (ah*bl));

        //[h[2*i-1], q_] = ts(q, t);
        // inlined (above line)
        const x = q + t;
        const bv = x - q;
        //h[2*i-1] = (q - (x - bv)) + (t - bv);
        //h.push((q - (x - bv)) + (t - bv));
        const hh = (q - (x - bv)) + (t - bv);
        if (hh !== 0) { h.push(hh); }
        q_ = x;

        //[h[2*i], q] = fts(T, q_);
        // inlined (above line)
        const xx = T + q_;
        //h[2*i] = q_ - (xx - T);
        //h.push(q_ - (xx - T));
        const hhh = q_ - (xx - T);
        if (hhh !== 0) { h.push(hhh); }
        q = xx;
    }

    //h[2*m - 1] = q;
    //h.push(q);
    if (q !== 0 || h.length === 0) { h.push(q); }

    //return eCompress(h);
    return h;
}


export { scaleExpansion, scaleExpansion2 }
