#include <iostream>
#include <map>
#include <set>
#include <algorithm>
#include <vector>
#include <sstream>
#include <string>
#include <functional>
#include <queue>
#include <deque>
#include <stack>
#include <limits>
#include <unordered_map>
#include <unordered_set>
#include <cmath>
#include <fstream>
#include <iterator>
#include <random>
#include <chrono>


#define forr(i,start,count) for (int i = (start); i < (start)+(count); ++i)
#define set_map_includes(set, elt) (set.find((elt)) != set.end())
#define readint(i) int i; cin >> i
#define readll(i) ll i; cin >> i
#define readdouble(i) double i; cin >> i
#define readstring(s) string s; cin >> s

typedef long long ll;

using namespace std;

ll modd = 1000 * 1000 * 1000 + 7;

template<class T>
class depth_first_search_iterator {
  public:
    stack<pair<T,T>> todo;
    function<bool(T)> notvisited;
    function<void(T)> mark_visited;
    function<set<T>(T)> neighbors;

    depth_first_search_iterator() {}

    depth_first_search_iterator(T start, function<bool(T)> notv, function<void(T)> mark_v, function<set<T>(T)> neigh) :
      notvisited(notv), mark_visited(mark_v), neighbors(neigh), end_(false), current(start), prev(start) {
          todo.push(make_pair(start, start)); operator++();
    }

    void operator++() {
        end_ = true;
        while (!todo.empty()) {
            current = todo.top().first; prev = todo.top().second; todo.pop();
            if (notvisited(current)) {
                mark_visited(current);
                for (const T x : neighbors(current)) {  todo.push(make_pair(x, current));   }
                end_ = false;
                break;
            }
        }
    }

    T operator*() {     return current;    }

    T previous() {  return prev;  }

    bool end() {  return end_;  }

  private:
    T current; // contains current node
    T prev;    // contains previous node
    bool end_;
};

template<class T>
class breadth_first_search_iterator {
  public:
    queue<pair<T,T>> todo;
    function<bool(T)> notvisited;
    function<void(T)> mark_visited;
    function<set<T>(T)> neighbors;

    breadth_first_search_iterator() {}

    breadth_first_search_iterator(T start, function<bool(T)> notv, function<void(T)> mark_v, function<set<T>(T)> neigh) :
      notvisited(notv), mark_visited(mark_v), neighbors(neigh), end_(false), current(start), prev(start) {
          todo.push(make_pair(start, start)); operator++();
    }

    void operator++() {
        end_ = true;
        while (!todo.empty()) {
            current = todo.front().first; prev = todo.front().second; todo.pop();
            if (notvisited(current)) {
                mark_visited(current);
                for (const T x : neighbors(current)) {  todo.push(make_pair(x, current));   }
                end_ = false;
                break;
            }
        }
    }

    T operator*() {     return current;    }

    T previous() {  return prev;  }

    bool end() {  return end_;  }

  private:
    T current;   // current node
    T prev;      // previous node
    bool end_;
};

template<class T>
class Graph {
    // allows only single connection between two vertices
    public:
      vector<T> vertices;
      vector<set<int>> neighbors;
      depth_first_search_iterator<int> dfs_iterator;
      breadth_first_search_iterator<int> bfs_iterator;
      vector<bool> visited;

      Graph() {}

      Graph(int n, T default_val = 0) {  for (int i = 0; i < n; ++i) {  AddVertex(default_val);  }   }

      void AddVertex(T val) {
          vertices.push_back(val);
          neighbors.push_back(set<int>());      }

      bool EdgeExists(int i, int j) {
          return (neighbors[i].find(j) != neighbors[i].end());
     }

      void AddEdge(int i, int j) {
          if (!EdgeExists(i,j)) {
            neighbors[i].insert(j);
            if (i != j) {         neighbors[j].insert(i);         } 
          }
      }

      T& operator[](int i) {      return vertices[i];      }

      void RemoveEdge(int i, int j) {
          if (EdgeExists(i,j)) {
            neighbors[i].erase(find(neighbors[i].begin(), neighbors[i].end(), j));
            neighbors[j].erase(find(neighbors[j].begin(), neighbors[j].end(), i));
          }
      }

      int vertices_count() {   return vertices.size();  }

      void dfs_init(int start) {
          visited = vector<bool>(vertices.size(), false);
          dfs_iterator = depth_first_search_iterator<int>(start, [this](int x){ return !visited[x]; }, [this](int x) { visited[x] = true;  },
            [this](int x) {  return neighbors[x];  } );
      }

      void bfs_init(int start) {
          visited = vector<bool>(vertices.size(), false);
          bfs_iterator = breadth_first_search_iterator<int>(start, [this](int x){ return !visited[x]; }, [this](int x) { visited[x] = true;  },
            [this](int x) {  return neighbors[x];  } );
      }

      void print_() {
          forr(i, 0, vertices.size()) {
              for (auto x : neighbors[i]) {
                  if (i <= x) {  cout << i << "-" << x << endl;  }                  
              }
          }
      }
};


template<class T>
class DirectedGraph {
    // allows only single connection between two vertices
    public:
      vector<T> vertices;
      vector<set<int>> neighbors;
      depth_first_search_iterator<int> dfs_iterator;
      breadth_first_search_iterator<int> bfs_iterator;
      vector<bool> visited;

      DirectedGraph() {}

      DirectedGraph(int n, T default_val = 0) {  for (int i = 0; i < n; ++i) {  AddVertex(default_val);  }   }

      bool EdgeExists(int i_from, int j_to) {
          return (neighbors[i_from].find(j_to) != neighbors[i_from].end());
      }

      void AddVertex(T val) {
          vertices.push_back(val);
          neighbors.push_back(set<int>());      }

      void AddEdge(int i_from, int j_to) {
          if (!EdgeExists(i_from, j_to)) {     neighbors[i_from].insert(j_to);      }
      }

      T& operator[](int i) {      return vertices[i];      }

      void RemoveEdge(int i_from, int j_to) {
          if (EdgeExists(i_from, j_to)) {
          neighbors[i_from].erase(find(neighbors[i_from].begin(), neighbors[i_from].end(), j_to));      } }

      DirectedGraph<T> Transpose() {
          DirectedGraph<T> ret;
          for (auto x : vertices) {  ret.AddVertex(x);   }
          for (int i_from = 0; i_from < vertices.size(); ++i_from) {
              for (int j_to : neighbors[i_from]) {
                  ret.AddEdge(j_to, i_from);
              }
          }
          return ret;      }

      int vertices_count() {   return vertices.size();  }

      void dfs_init(int start) {
          visited = vector<bool>(vertices.size(), false);
          dfs_iterator = depth_first_search_iterator<int>(start, [this](int x){ return !visited[x]; }, [this](int x) { visited[x] = true;  },
            [this](int x) {  return neighbors[x];  } );
      }

      void bfs_init(int start) {
          visited = vector<bool>(vertices.size(), false);
          bfs_iterator = breadth_first_search_iterator<int>(start, [this](int x){ return !visited[x]; }, [this](int x) { visited[x] = true;  },
            [this](int x) {  return neighbors[x];  } );
      }

      void print_() {
          forr(i, 0, vertices.size()) {
              for (auto x : neighbors[i]) {
                  cout << i << "->" << x << endl;
              }
          }
      }

};

template<class T>
class SegmentTree2 {
    public:
     
      SegmentTree2(const vector<T>& data, function<T(T,T)> f_, T zero_value = 0) : zv(zero_value), f(f_) {   
          Initialize(data);
      }

      SegmentTree2(int n, function<T(T,T)> f_, T zero_value = 0) : zv(zero_value), f(f_) {
          vector<T> temp(n, zv);
          Initialize(temp);
      }

      T operator[](int i) {
          return tree[B+i];
      }

      T GetEvaluation(int L, int R) {   // "min/max" on interval [L,R); 0-based indexing, as usual
        if (R<=L) {  return zv;  }
        return GetEvaluationHelper(L, R, 0, B, 1);
      }


      void SetVal(int i, T val) {
			tree[B+i] = val;
			for(int j = (B + i) / 2; j >= 1; j /= 2) {
				tree[j] = f(tree[2*j], tree[2*j+1]);
			}
      }

      private:
        vector<T> tree;
        int B;  // power of two greater than size of input data
        T zv;
        function<T(T,T)> f;

        void Initialize(const vector<T>& data) {
    	  B = 1;
          while(B < data.size()) {B *= 2;  }
          tree = std::move(vector<T>(2*B, zv));
          copy(data.begin(), data.end(), next(tree.begin(), B));
          for(int i = B - 1; i >= 1; --i) {
  	  	     tree[i] = f(tree[2*i], tree[2*i+1]);
          }
        }

      T GetEvaluationHelper(int L, int R, int start, int length, int tree_index) {
          if (L==R) {  return zv; }
          if ((L==start) && (R==start+length)) {   return tree[tree_index];  }
          int midpoint = start + length/2;

          T left_ = zv, right_ = zv;
          if (L<=min(midpoint,R)) {
            left_ = GetEvaluationHelper(L, min(midpoint,R), start, length/2, 2*tree_index);
          }
          if (max(midpoint,L)<=R) {
            right_ = GetEvaluationHelper(max(midpoint,L), R, midpoint, length/2, 2*tree_index+1);
          }
          return f(left_, right_);
      }

};

template<class T>
class HLD {
    public:
      vector<vector<int>> hld_paths;  // hld paths of vertices; each path begins above and goes down
      vector<pair<int,int>> dictionary; // dictionary[u].first = index of hld path (ie 1st index of hld_paths) that includes vertex u
                                        // dictionary[u].second = index of u in hld path
      vector<int> parent;    // parent[u]=parent node of u; parent[root] = -1
      DirectedGraph<int> compressed_graph;   // create only if you need "LCA" type services; edges go down;
                                            // take transpose or use parents for opposite
                                            // values in compressed graph are depths of nodes in compressed graphs
                                            // root is 0

      HLD(DirectedGraph<T>& g, int root, bool create_compressed_graph = false) : dictionary(g.vertices_count()), parent(g.vertices_count(),-1) {
          vector<int> subtree(g.vertices_count());
          function<void(int)> f = [&f,&g,&subtree,this](int u) {
              int tot = 0;
              for(auto x : g.neighbors[u]) {
                  parent[x] = u;
                  f(x); tot += subtree[x];
              }
              subtree[u] = 1 + tot;
          };
          f(root);
          stack<int> todo; todo.push(root);
          while (!todo.empty()) {
              int curr = todo.top(); todo.pop();
              hld_paths.push_back(vector<int>());
              while (true) {
                  hld_paths.back().push_back(curr);
                  if (g.neighbors[curr].empty()) { break; }
                  int largest_u, max_ = 0;
                  for(auto x : g.neighbors[curr]) {
                      if (subtree[x]>max_) {
                          max_ = subtree[x]; largest_u = x;
                      }
                  }
                  for(auto x : g.neighbors[curr]) {
                      if (x!= largest_u) {  todo.push(x);  }
                  }
                  curr = largest_u;
              }
          }
          forr(i,0,hld_paths.size()) {
              forr(j,0,hld_paths[i].size()) {
                  dictionary[hld_paths[i][j]] = make_pair(i,j);
              }
          }
          if (create_compressed_graph)  {
              compressed_graph = DirectedGraph<int>(hld_paths.size());
              forr(i,0,g.vertices_count()) {
                  if (parent[i]!=-1) {
                      if (dictionary[i].first!=dictionary[parent[i]].first) {
                          compressed_graph.AddEdge(dictionary[parent[i]].first, dictionary[i].first);
                      }
                  }
              }
              compressed_graph.bfs_init(0); ++compressed_graph.bfs_iterator;
              while (!compressed_graph.bfs_iterator.end()) {
                  compressed_graph[*compressed_graph.bfs_iterator] = 1 + compressed_graph[compressed_graph.bfs_iterator.previous()];
                  ++compressed_graph.bfs_iterator;
              }
          }
      }

      int LCA(int i, int j) {
          if (compressed_graph.vertices_count()==0) {  cout << "Compressed graph not created!" << endl;   }
          while (dictionary[i].first!=dictionary[j].first) {
              if (compressed_graph[dictionary[i].first] > compressed_graph[dictionary[j].first]) {
                  i = parent[  hld_paths[dictionary[i].first][0]  ];
              } else {
                  j = parent[  hld_paths[dictionary[j].first][0] ];
              }
          }
          if (dictionary[i].second > dictionary[j].second) {
              return j;
          } else {
              return i;
          }
      }

      void print_() {
          for(auto x : hld_paths) {
              for(auto y : x) {
                  cout << y << " ";
              }
              cout << endl;
          }
      }

};

template<class T>
T fastpower(T a, long long n) {  // n must be >= 1
  if (n==0) {  return 1;  }
  if (n % 2 == 0) {  
      T halfpower = fastpower<T>(a, n/2);
      return (halfpower*halfpower) % modd;
  } else {
      if (n == 1) {  return a; } else {  
          T halfpower = fastpower<T>(a, n/2);
          return (((halfpower*halfpower)%modd)*a)%modd;
      }
  }
};


int main()   {

    cout.precision(17);
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    uniform_int_distribution<int> rand_gen(0, modd);   // rand_gen(rng) gets the rand no

//    readint(test_cases);
    int test_cases = 1;
    forr(t, 1, test_cases) {
        readint(n); readint(q);
        Graph<int> g_(n);
        DirectedGraph<pair<ll,ll>> g(n, make_pair(0,0));
        vector<tuple<int,int,int,int>> edges;
        forr(i,0,n-1) {
            readint(aa); readint(bb); readint(cc); readint(dd);
            edges.push_back(make_tuple(aa-1,bb-1,cc,dd));
            g_.AddEdge(aa-1,bb-1);
        }
        function<void(int,int)> f = [&g,&g_,&f](int root, int parent) {
            if (parent>=0) {  g.AddEdge(parent, root);  }
            for(auto x : g_.neighbors[root]) {
                if (x!=parent) {   f(x,root);   }
            }
        };
        f(0, -1);
        for(auto x : edges) {
            if (g.EdgeExists(get<0>(x), get<1>(x))) {
                g[get<1>(x)] = make_pair(get<2>(x), get<3>(x));
            } else {
                g[get<0>(x)] = make_pair(get<2>(x), get<3>(x));
            }
        }
        vector<ll> distance(n);
        g.dfs_init(0); ++g.dfs_iterator;
        while (!g.dfs_iterator.end()) {
            distance[*g.dfs_iterator] = distance[g.dfs_iterator.previous()] + g[*g.dfs_iterator].second;
            ++g.dfs_iterator;
        }

        HLD<pair<ll,ll>> hld(g, 0, true);
        vector<SegmentTree2<ll>> seg;
        forr(i,0,hld.hld_paths.size()) {
            SegmentTree2<ll> temp(hld.hld_paths[i].size(), [](int x, int y){return x+y;}, 0);
            seg.push_back(temp);
        }
        vector<vector<pair<ll,ll>>> col_dist(n);
        forr(i,1,n-1) {
            col_dist[g[i].first].push_back(make_pair(g[i].second, i));
        }

        vector<ll> ans(q);
        vector<vector<int>> colors(n);
        vector<tuple<int,ll,int,int>> query;
        forr(i,0,q) {
            readint(c); readint(y); readint(u); readint(v); u--; v--;
            ans[i] = distance[u] + distance[v] - 2*distance[hld.LCA(u,v)];
            query.push_back(make_tuple(c,y,u,v));
            colors[c].push_back(i);
        }
        forr(c,1,n-1) {
            if (colors[c].empty()) {continue;}
            for(auto x : col_dist[c]) {
                seg[ hld.dictionary[x.second].first ].SetVal(hld.dictionary[x.second].second, x.first);
            }
            for(auto x : colors[c]) {
                int curr = get<2>(query[x]);
                while (curr>=0) {
                  ans[x] -= seg[hld.dictionary[curr].first].GetEvaluation(0, 1+hld.dictionary[curr].second);
                  curr = hld.parent[ hld.hld_paths[hld.dictionary[curr].first][0]  ];
                }
                curr = get<3>(query[x]);
                while (curr>=0) {
                  ans[x] -= seg[hld.dictionary[curr].first].GetEvaluation(0, 1+hld.dictionary[curr].second);
                  curr = hld.parent[ hld.hld_paths[hld.dictionary[curr].first][0]  ];
                }
                curr = hld.LCA(get<2>(query[x]), get<3>(query[x]));
                while (curr>=0) {
                  ans[x] += 2*seg[hld.dictionary[curr].first].GetEvaluation(0, 1+hld.dictionary[curr].second);
                  curr = hld.parent[ hld.hld_paths[hld.dictionary[curr].first][0]  ];
                }
            }
            for(auto x : col_dist[c]) {
                seg[ hld.dictionary[x.second].first ].SetVal(hld.dictionary[x.second].second, 1);
            }
            for(auto x : colors[c]) {
                int curr = get<2>(query[x]);
                while (curr>=0) {
                  ans[x] += seg[hld.dictionary[curr].first].GetEvaluation(0, 1+hld.dictionary[curr].second) * get<1>(query[x]);
                  curr = hld.parent[ hld.hld_paths[hld.dictionary[curr].first][0]  ];
                }
                curr = get<3>(query[x]);
                while (curr>=0) {
                  ans[x] += seg[hld.dictionary[curr].first].GetEvaluation(0, 1+hld.dictionary[curr].second) * get<1>(query[x]);
                  curr = hld.parent[ hld.hld_paths[hld.dictionary[curr].first][0]  ];
                }
                curr = hld.LCA(get<2>(query[x]), get<3>(query[x]));
                while (curr>=0) {
                  ans[x] -= 2*seg[hld.dictionary[curr].first].GetEvaluation(0, 1+hld.dictionary[curr].second) * get<1>(query[x]);
                  curr = hld.parent[ hld.hld_paths[hld.dictionary[curr].first][0]  ];
                }
            }
            for(auto x : col_dist[c]) {
                seg[ hld.dictionary[x.second].first ].SetVal(hld.dictionary[x.second].second, 0);
            }
        }

        forr(i,0,q) {
            cout << ans[i] << endl;
        }

    }
    return 0;
}


Measured memory_bytes: 71352