Problem: There are n casinos lined in a row. If Memory plays at casino i, he has probability pi to win and move to the casino on the right (i + 1) or exit the row (if i = n), and a probability 1 - pi to lose and move to the casino on the left (i - 1) or also exit the row (if i = 1). 

We say that Memory dominates on the interval i... j if he completes a walk such that,

  * He starts on casino i. 
  * He never looses in casino i. 
  * He finishes his walk by winning in casino j. 



Note that Memory can still walk left of the 1-st casino and right of the casino n and that always finishes the process.

Now Memory has some requests, in one of the following forms:

  * 1 i a b: Set <image>. 
  * 2 l r: Print the probability that Memory will dominate on the interval l... r, i.e. compute the probability that Memory will first leave the segment l... r after winning at casino r, if she starts in casino l. 



It is guaranteed that at any moment of time p is a non-decreasing sequence, i.e. pi ≤ pi + 1 for all i from 1 to n - 1.

Please help Memory by answering all his requests!

Input

The first line of the input contains two integers n and q(1 ≤ n, q ≤ 100 000), — number of casinos and number of requests respectively.

The next n lines each contain integers ai and bi (1 ≤ ai < bi ≤ 109) — <image> is the probability pi of winning in casino i.

The next q lines each contain queries of one of the types specified above (1 ≤ a < b ≤ 109, 1 ≤ i ≤ n, 1 ≤ l ≤ r ≤ n).

It's guaranteed that there will be at least one query of type 2, i.e. the output will be non-empty. Additionally, it is guaranteed that p forms a non-decreasing sequence at all times.

Output

Print a real number for every request of type 2 — the probability that boy will "dominate" on that interval. Your answer will be considered correct if its absolute error does not exceed 10 - 4.

Namely: let's assume that one of your answers is a, and the corresponding answer of the jury is b. The checker program will consider your answer correct if |a - b| ≤ 10 - 4.

Example

Input

3 13
1 3
1 2
2 3
2 1 1
2 1 2
2 1 3
2 2 2
2 2 3
2 3 3
1 2 2 3
2 1 1
2 1 2
2 1 3
2 2 2
2 2 3
2 3 3


Output

0.3333333333
0.2000000000
0.1666666667
0.5000000000
0.4000000000
0.6666666667
0.3333333333
0.2500000000
0.2222222222
0.6666666667
0.5714285714
0.6666666667

Solution (in JAVA):

Solution:
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Jialin Ouyang (Jialin.Ouyang@gmail.com)
 */
public class Main {
  public static void main(String[] args) {
    InputStream inputStream = System.in;
    OutputStream outputStream = System.out;
    QuickScanner in = new QuickScanner(inputStream);
    QuickWriter out = new QuickWriter(outputStream);
    TaskE solver = new TaskE();
    solver.solve(1, in, out);
    out.close();
  }

  static class TaskE {
    int n;
    double[] p;
    IT it;

    public void solve(int testNumber, QuickScanner in, QuickWriter out) {
      n = in.nextInt();
      p = new double[n];
      int queryCnt = in.nextInt();
      for (int i = 0; i < n; ++i) {
        double a = in.nextInt();
        p[i] = a / in.nextInt();
      }
      it = new IT(n);
      it.init(n);
      for (int queryIdx = 0; queryIdx < queryCnt; ++queryIdx) {
        if (in.nextInt() == 1) {
          int idx = in.nextInt() - 1;
          double a = in.nextInt();
          p[idx] = a / in.nextInt();
          it.updateRange(idx, idx + 1);
        } else {
          int lower = in.nextInt() - 1;
          int upper = in.nextInt();
          out.printf("%.10f\n", it.calcWin(lower, upper));
        }
      }
    }

    class IT extends AbstractIntervalTree {
      double[] l;
      double[] r;
      double resL;
      double resR;

      IT(int n) {
        l = new double[n << 2];
        r = new double[n << 2];
      }

      public double calcWin(int lower, int upper) {
        resL = 1;
        resR = 0;
        calc(lower, upper);
        return resL;
      }


      public void initLeaf(int nodeIdx, int idx) {
        l[nodeIdx] = r[nodeIdx] = p[idx];
      }


      public void merge(int nodeIdx, int leftNodeIdx, int rightNodeIdx) {
        l[nodeIdx] = mergeL(l[leftNodeIdx], r[leftNodeIdx], l[rightNodeIdx], r[rightNodeIdx]);
        r[nodeIdx] = mergeR(l[leftNodeIdx], r[leftNodeIdx], l[rightNodeIdx], r[rightNodeIdx]);
      }


      public void updateNode(int nodeIdx, int lower, int upper) {
        initLeaf(nodeIdx, lower);
      }


      public void calcAppend(int nodeIdx, int lower, int upper) {
        double tmpL = mergeL(resL, resR, l[nodeIdx], r[nodeIdx]);
        resR = mergeR(resL, resR, l[nodeIdx], r[nodeIdx]);
        resL = tmpL;
      }


      public void pushLazyPropagation(int fromNodeIdx, int toNodeIdx) {
      }


      public void clearLazyPropagation(int nodeIdx) {
      }

      double mergeL(double l1, double r1, double l2, double r2) {
        return l1 * l2 / (1 - r1 + l2 * r1);
      }

      double mergeR(double l1, double r1, double l2, double r2) {
        return r2 + (1 - r2) * r1 * l2 / (1 - r1 + l2 * r1);
      }

    }

  }

  static class QuickScanner {
    private static final int BUFFER_SIZE = 1024;
    private InputStream stream;
    private byte[] buffer;
    private int currentPosition;
    private int numberOfChars;

    public QuickScanner(InputStream stream) {
      this.stream = stream;
      this.buffer = new byte[BUFFER_SIZE];
      this.currentPosition = 0;
      this.numberOfChars = 0;
    }

    public int nextInt() {
      int c = nextNonSpaceChar();
      boolean positive = true;
      if (c == '-') {
        positive = false;
        c = nextChar();
      }
      int res = 0;
      do {
        if (c < '0' || '9' < c) throw new RuntimeException();
        res = res * 10 + c - '0';
        c = nextChar();
      } while (!isSpaceChar(c));
      return positive ? res : -res;
    }

    public int nextNonSpaceChar() {
      int res = nextChar();
      for (; isSpaceChar(res) || res < 0; res = nextChar()) ;
      return res;
    }

    public int nextChar() {
      if (numberOfChars == -1) {
        throw new RuntimeException();
      }
      if (currentPosition >= numberOfChars) {
        currentPosition = 0;
        try {
          numberOfChars = stream.read(buffer);
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
        if (numberOfChars <= 0) {
          return -1;
        }
      }
      return buffer[currentPosition++];
    }

    public boolean isSpaceChar(int c) {
      return c == ' '
          || c == '\n'
          || c == '\r'
          || c == '\t'
          || c < 0;
    }

  }

  static class QuickWriter {
    private final PrintWriter writer;

    public QuickWriter(OutputStream outputStream) {
      this.writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
    }

    public QuickWriter(Writer writer) {
      this.writer = new PrintWriter(writer);
    }

    public void printf(String format, Object... objects) {
      writer.printf(format, objects);
    }

    public void close() {
      writer.close();
    }

  }

  static abstract class AbstractIntervalTree {
    private int n;
    private int left;
    private int right;

    public abstract void initLeaf(int nodeIdx, int idx);

    public abstract void merge(int nodeIdx, int leftNodeIdx, int rightNodeIdx);

    public abstract void updateNode(int nodeIdx, int lower, int upper);

    public abstract void calcAppend(int nodeIdx, int lower, int upper);

    public abstract void pushLazyPropagation(int fromNodeIdx, int toNodeIdx);

    public abstract void clearLazyPropagation(int nodeIdx);

    public void init(int n) {
      this.n = n;
      init(0, 0, n);
    }

    public void updateRange(int lower, int upper) {
      left = lower;
      right = upper;
      updateRange(0, 0, n);
    }

    public void calc(int lower, int upper) {
      left = lower;
      right = upper;
      calc(0, 0, n);
    }

    private void init(int nodeIdx, int lower, int upper) {
      if (lower + 1 == upper) {
        initLeaf(nodeIdx, lower);
        return;
      }
      int medium = (lower + upper) >> 1;
      init(toLeft(nodeIdx), lower, medium);
      init(toRight(nodeIdx), medium, upper);
      merge(nodeIdx, toLeft(nodeIdx), toRight(nodeIdx));
    }

    private void updateRange(int nodeIdx, int lower, int upper) {
      if (left <= lower && upper <= right) {
        updateNode(nodeIdx, lower, upper);
        return;
      }
      pushLazyPropagation(nodeIdx);
      int medium = (lower + upper) >> 1;
      if (left < medium) {
        updateRange(toLeft(nodeIdx), lower, medium);
      }
      if (medium < right) {
        updateRange(toRight(nodeIdx), medium, upper);
      }
      merge(nodeIdx, toLeft(nodeIdx), toRight(nodeIdx));
    }

    private void calc(int nodeIdx, int lower, int upper) {
      if (left <= lower && upper <= right) {
        calcAppend(nodeIdx, lower, upper);
        return;
      }
      pushLazyPropagation(nodeIdx);
      int medium = (lower + upper) >> 1;
      if (left < medium) {
        calc(toLeft(nodeIdx), lower, medium);
      }
      if (medium < right) {
        calc(toRight(nodeIdx), medium, upper);
      }
      merge(nodeIdx, toLeft(nodeIdx), toRight(nodeIdx));
    }

    private void pushLazyPropagation(int nodeIdx) {
      pushLazyPropagation(nodeIdx, toLeft(nodeIdx));
      pushLazyPropagation(nodeIdx, toRight(nodeIdx));
      clearLazyPropagation(nodeIdx);
    }

    private int toLeft(int nodeIdx) {
      return (nodeIdx << 1) | 1;
    }

    private int toRight(int nodeIdx) {
      return (nodeIdx + 1) << 1;
    }

  }
}