static long queryRangeSum(int currNode, int tSI, int tEI, int rSI, int rEI) {
    Node temp = tree[currNode];

    /*		System.out.println("Q-----currNode : " + currNode + ", tSI : " + tSI
    + ", tEI : " + tEI + ", tree[cN].sum : " + tree[currNode].sum);*/

    if (temp.init != -1) {
      // System.out.println("currNode : " + currNode + ", in init");
      temp.sum = ((tEI - tSI + 1) * temp.init);
      temp.sum %= mod;

      if (tSI != tEI) {
        tree[2 * currNode].init = temp.init;
        tree[2 * currNode + 1].init = temp.init;
      }

      temp.init = -1;
      temp.add = 0;
      temp.mul = 0;
    } else {
      temp.sum = (temp.sum * temp.mul) + ((tEI - tSI + 1) * temp.add);
      temp.sum %= mod;

      if (tSI != tEI) {
        tree[2 * currNode].add += temp.add;
        tree[2 * currNode].add %= mod;
        tree[2 * currNode].mul *= temp.mul;
        tree[2 * currNode].mul %= mod;
        tree[2 * currNode + 1].add += temp.add;
        tree[2 * currNode + 1].add %= mod;
        tree[2 * currNode + 1].mul *= temp.mul;
        tree[2 * currNode + 1].mul %= mod;
      }

      temp.add = 0;
      temp.mul = 1;
    }

    if (tSI > tEI || tSI > rEI || tEI < rSI) return -1;

    if (tSI >= rSI && tEI <= rEI) return temp.sum;

    int mid = (tSI + tEI) / 2;
    long lCS, rCS;

    lCS = queryRangeSum(2 * currNode, tSI, mid, rSI, rEI);
    rCS = queryRangeSum(2 * currNode + 1, mid + 1, tEI, rSI, rEI);

    if (lCS == -1) lCS = 0;

    if (rCS == -1) rCS = 0;

    tree[currNode].sum = tree[2 * currNode].sum + tree[2 * currNode + 1].sum;
    tree[currNode].sum %= mod;

    return (lCS + rCS) % mod;
  }
  static void addToRange(
      int currNode, int tSI, int tEI, int rSI, int rEI, long addValue) // fine, I guess
      {
    Node temp = tree[currNode];

    if (temp.add > 0 || temp.mul > 1) {
      temp.sum = (temp.sum * temp.mul) + ((tEI - tSI + 1) * temp.add);
      temp.sum %= mod;

      if (tSI != tEI) {
        tree[2 * currNode].add += temp.add;
        tree[2 * currNode].add %= mod;
        tree[2 * currNode].mul *= temp.mul;
        tree[2 * currNode].mul %= mod;
        tree[2 * currNode + 1].add += temp.add;
        tree[2 * currNode + 1].add %= mod;
        tree[2 * currNode + 1].mul *= temp.mul;
        tree[2 * currNode + 1].mul %= mod;
      }

      temp.add = 0;
      temp.mul = 1;
    }

    if (tSI > tEI || tSI > rEI || tEI < rSI) return;

    if (tSI >= rSI && tEI <= rEI) {
      temp.sum = temp.sum * temp.mul + ((tEI - tSI + 1) * addValue);
      temp.sum %= mod;

      if (tSI != tEI) {
        tree[2 * currNode].add += addValue;
        tree[2 * currNode].add %= mod;
        tree[2 * currNode].mul *= temp.mul;
        tree[2 * currNode].mul %= mod;
        tree[2 * currNode + 1].add += addValue;
        tree[2 * currNode + 1].add %= mod;
        tree[2 * currNode + 1].mul *= temp.mul;
        tree[2 * currNode + 1].mul %= mod;
      }

      temp.mul = 1;

      return;
    }

    int mid = (tSI + tEI) / 2;

    addToRange(2 * currNode, tSI, mid, rSI, rEI, addValue);
    addToRange(2 * currNode + 1, mid + 1, tEI, rSI, rEI, addValue);

    temp.sum = tree[2 * currNode].sum + tree[2 * currNode + 1].sum;
    temp.sum %= mod;
  }
  static void multiplyToRange(int currNode, int tSI, int tEI, int rSI, int rEI, long mulValue) {
    Node temp = tree[currNode];
    /*
    	System.out.println("MUL currNode : " + currNode + ", tSI : " + tSI
    			+ ", tEI : " + tEI + ", sum : " + temp.sum + ", mul : " + temp.mul);
    */
    if (temp.mul > 1 || temp.add > 0) {
      temp.sum = temp.sum * temp.mul + temp.add;
      temp.sum %= mod;

      // System.out.println("mul done. sum : " + temp.sum);

      if (tSI != tEI) {
        tree[2 * currNode].mul *= temp.mul;
        tree[2 * currNode].mul %= mod;
        tree[2 * currNode].add *= temp.mul;
        tree[2 * currNode].add %= mod;
        tree[2 * currNode + 1].mul *= temp.mul;
        tree[2 * currNode + 1].mul %= mod;
        tree[2 * currNode + 1].add *= temp.mul;
        tree[2 * currNode + 1].add %= mod;
      }

      temp.mul = 1;
      temp.add = 0;
    }

    if (tSI > tEI || tSI > rEI || tEI < rSI) return;

    if (tSI >= rSI && tEI <= rEI) {
      temp.sum = temp.sum * mulValue + temp.add * mulValue;
      temp.sum %= mod;

      /*			System.out.println("fully inside, sum : " + temp.sum + ", tSI : "
      + tSI + ", tEI : " + tEI);*/

      if (tSI != tEI) {
        tree[2 * currNode].mul *= mulValue;
        tree[2 * currNode].mul %= mod;
        tree[2 * currNode + 1].mul *= mulValue;
        tree[2 * currNode + 1].mul %= mod;
      }

      temp.add = 0;
      // not needed
      // temp.mul = 1;

      return;
    }

    int mid = (tSI + tEI) / 2;

    multiplyToRange(2 * currNode, tSI, mid, rSI, rEI, mulValue);
    multiplyToRange(2 * currNode + 1, mid + 1, tEI, rSI, rEI, mulValue);

    temp.sum = tree[2 * currNode].sum + tree[2 * currNode + 1].sum;
    temp.sum %= mod;
  }
  static void initializeRange(int currNode, int tSI, int tEI, int rSI, int rEI, long initValue) {
    Node temp = tree[currNode];

    if (temp.init != -1) {
      temp.sum = ((tEI - tSI + 1) * temp.init);
      temp.sum %= mod;

      if (tSI != tEI) {
        tree[2 * currNode].init = temp.init;
        tree[2 * currNode + 1].init = temp.init;
      }

      temp.init = -1;
      temp.add = 0;
      temp.mul = 1;
    } else if (temp.add > 0 || temp.mul > 1) {
      temp.sum = temp.sum * temp.mul + ((tEI - tSI + 1) * temp.add);
      temp.sum %= mod;

      if (tSI != tEI) {
        tree[2 * currNode].add += temp.add;
        tree[2 * currNode].add %= mod;
        tree[2 * currNode + 1].add += temp.add;
        tree[2 * currNode + 1].add %= mod;
        tree[2 * currNode].mul *= temp.mul;
        tree[2 * currNode].mul %= mod;
        tree[2 * currNode + 1].mul *= temp.mul;
        tree[2 * currNode + 1].mul %= mod;
      }

      temp.init = -1;
      temp.add = 0;
      temp.mul = 1;
    }

    /*		System.out.println("INIT cN : " + currNode + ", tSI : " + tSI
    + ", tEI : " + tEI + ", rSI : " + rSI + ", rEI : " + rEI);*/

    if (tSI > tEI || tSI > rEI || tEI < rSI) return;

    if (tSI >= rSI && tEI <= rEI) {
      temp.sum = (tEI - tSI + 1) * initValue;
      temp.sum %= mod;

      if (tSI != tEI) {
        tree[2 * currNode].init = initValue;
        tree[2 * currNode + 1].init = initValue;
      }

      temp.add = 0;
      temp.mul = 1;

      return;
    }

    int mid = (tSI + tEI) / 2;

    initializeRange(2 * currNode, tSI, mid, rSI, rEI, initValue);
    initializeRange(2 * currNode + 1, mid + 1, tEI, rSI, rEI, initValue);

    tree[currNode].sum = tree[2 * currNode].sum + tree[2 * currNode + 1].sum;
    tree[currNode].sum %= mod;
  }