// Code By H~$~C

module main;

import core.stdc.stdlib, core.stdc.string;
import std.stdio, std.array, std.string, std.math, std.uni;
import std.algorithm, std.range, std.random, std.container;
import std.conv, std.typecons, std.functional;

immutable Maxn = 100005;

int n, q;
Array!(Tuple!(int, int, int))[Maxn] g;
int[Maxn] col, wd, dist;

int indx;
int[Maxn] dep, par, sz, son, top, ind, rind;
void dfs1(int u, int fa, int depth) {
  par[u] = fa, sz[u] = 1;
  dep[u] = depth;
  foreach(pr; g[u]) {
    int v = pr[0];
    if (v == fa) continue;
    dist[v] = dist[u] + pr[2];
    dfs1(v, u, depth + 1);
    sz[u] += sz[v];
    if (sz[v] > sz[son[u]]) son[u] = v;
    col[v] = pr[1];
    wd[v] = pr[2];
  }
}
void dfs2(int u, int topv) {
  top[u] = topv;
  ind[u] = ++indx;
  rind[indx] = u;
  if (son[u]) dfs2(son[u], topv);
  foreach(pr; g[u]) {
    int v = pr[0];
    if (v != par[u] && v != son[u])
      dfs2(v, v);
  }
}

// segment tree
struct node {
  this()() {
    cnt = 0;
    sum = 0;
  }
  this()(in node x) { *this = x; }
  int cnt, sum, ls, rs;
  void apply(int l, int r, int c, int s) {
    cnt = c, sum = s;
  }
}
void pullup(ref node a, in node b, in node c) {
  a.cnt = b.cnt + c.cnt;
  a.sum = b.sum + c.sum;
}
node[] tr;

struct segment_tree {
  int N, root;
  private void pushup(int p) {
    pullup(tr[p], tr[tr[p].ls], tr[tr[p].rs]);
  }
  private void modify_(T...)(ref int p, int l, int r, int L, int R, T args) {
    if (!p) p = cast(int)tr.length, tr.length++;
    if (L == l && r == R) return tr[p].apply(l, r, args);
    int mid = (l + r) >> 1;
    if (R <= mid) modify_(tr[p].ls, l, mid, L, R, args);
    else if (L > mid) modify_(tr[p].rs, mid + 1, r, L, R, args);
    else {
      modify_(tr[p].ls, l, mid, L, mid, args);
      modify_(tr[p].rs, mid + 1, r, mid + 1, R, args);
    }
    pushup(p);
  }
  private node query_(int p, int l, int r, int L, int R) {
    if (!p) return node.init;
    if (L == l && r == R) return tr[p];
    int mid = (l + r) >> 1;
    if (R <= mid) return query_(tr[p].ls, l, mid, L, R);
    if (L > mid) return query_(tr[p].rs, mid + 1, r, L, R);
    node left = query_(tr[p].ls, l, mid, L, mid);
    node right = query_(tr[p].rs, mid + 1, r, mid + 1, R);
    node ans; pullup(ans, left, right); return ans;
  }
  public void build(T...)(int len, T args) {
    N = len;
    root = 0;
  }
  public void modify(T...)(int L, int R, T args) {
    if (L > R) return ;
    modify_(root, 1, N, L, R, args);
  }
  public node query(int L, int R) {
    if (L > R) return node();
    return query_(root, 1, N, L, R);
  }
}
segment_tree[Maxn] sgt;

int jump_chain(int u, int v, int c, int d) {
  int U, V; U = u, V = v;
  node res;
  while (top[u] != top[v]) {
    if (dep[top[u]] > dep[top[v]]) swap(u, v);
    pullup(res, res, sgt[c].query(ind[top[v]], ind[v]));
    v = par[top[v]];
  }
  if (dep[u] > dep[v]) swap(u, v);
  pullup(res, res, sgt[c].query(ind[u] + 1, ind[v]));
  return dist[U] + dist[V] - dist[u] * 2 - res.sum + res.cnt * d;
}

void solve(in int testcase) {
  n.read, q.read;
  foreach(i; 1 .. n) {
    int u, v, d, c;
    u.read, v.read, c.read, d.read;
    g[u].insertBack(tuple(v, c, d));
    g[v].insertBack(tuple(u, c, d));
  }
  dfs1(1, 0, 1);
  dfs2(1, 1);
  tr.length = 1;
  foreach(i; 1 .. n) sgt[i].build(n);
  foreach(i; 2 .. n + 1) {
    sgt[col[i]].modify(ind[i], ind[i], 1, wd[i]);
  }
  while (q--) {
    int u, v, c, d;
    c.read, d.read, u.read, v.read;
    writeln(jump_chain(u, v, c, d));
  }
}

// ************************************************************

T reader(T)() { return readToken().to!T; }
void read(T)(ref T t) { t = reader!T; }
T[] readarray(T)() { return readln.splitter.map!(to!T).array; }
void chmax(T)(ref T a, T b) { a = max(a, b); }
void chmin(T)(ref T a, T b) { a = min(a, b); }
alias less = (a, b) => a < b;
alias greater = (a, b) => a > b;
T min_element(T)(in T[] a) { return a.element!less; }
T max_element(T)(in T[] a) { return a.element!greater; }
alias unique = uniq;

// constant or some variables
immutable int inf = 0x3f3f3f3f;
immutable long lnf = 0x3f3f3f3f3f3f3f3f;
immutable typeof(null) NULL = null;
string[] _tokens;

// functions
T element(alias pred, T)(in T[] a) {
  int pos = 0;
  for (int i = 1; i < cast(int)(a.length); ++i)
    if (binaryFun!pred(a[i], a[pos])) pos = i;
  return a[pos];
}
string readToken() {
  for (; _tokens.empty; ) {
    if (stdin.eof) return "";
    _tokens = readln.split;
  }
  auto token = _tokens.front;
  _tokens.popFront;
  return token;
}
int binary_bearch(alias pred, T)(in T[] as) {
  int l = -1, h = cast(int)(as.length), mid;
  for (; l + 1 < h; ) {
    mid = (l + h) >> 1;
    (unaryFun!pred(as[mid]) ? h : l) = mid;
  }
  return h;
}
int lower_bound(alias pred = less, T)(in T[] as, T val) {
  return as.binary_bearch!(a => a == val || pred(val, a));
}
int upper_bound(alias pred = less, T)(in T[] as, T val) {
  return as.binary_bearch!(a => pred(val, a));
}

void main(string[] args) {
  int tests = 1;
//  tests.read();
  foreach(test; 1 .. tests + 1) solve(test);
}
