//
//  main.cpp
//
// https://atcoder.jp/contests/abc133/tasks/abc133_f

#include <algorithm>
#include <array>
#include <assert.h>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <math.h>
#include <memory>
#include <queue>
#include <random>
#include <set>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

using ll = long long;
using ull = unsigned long long;

struct Graph {
    struct Edge {
        size_t from;
        size_t to;
        int color;
        int distance;
    };
    vector<vector<Edge>> edges;
    Graph(size_t size) {
        edges.resize(size);
    }
    void add_edge(size_t from, size_t to, int color, int distance) {
        edges[from].push_back(Edge{from, to, color, distance});
    }
};

int main(int argc, char* argv[]) {
    int N, Q;
    cin >> N >> Q;
    Graph tree(N);
    for (int i = 0; i < N-1; i++) {
        size_t ai, bi;
        int color, distance;
        cin >> ai >> bi >> color >> distance;
        ai--;
        bi--;
        tree.add_edge(ai, bi, color, distance);
        tree.add_edge(bi, ai, color, distance);
    }
    vector<size_t> flatten_index; // first_visit_index[idx] = 元のグラフの頂点idxが平坦化されたグラフに現れる最初のインデックス
    flatten_index.resize(N);
    struct FlatTreeEdge {
        int color;
        int distance;
    };
    vector<FlatTreeEdge> flat_tree;
    {
        vector<bool> visited;
        visited.resize(N, false);
        struct State {
            bool back;
            size_t vertex;
            int color;
            int distance;
        };
        vector<State> stack;
        stack.push_back(State{false, 0, 0, 0});
        while (!stack.empty()) {
            State state = stack.back();
            stack.pop_back();
            if (!(state.vertex == 0 && !state.back)) { // 最初以外のとき
                flat_tree.push_back(FlatTreeEdge{state.color, state.distance});
            }
            if (!visited[state.vertex]) {
                flatten_index[state.vertex] = flat_tree.size();
            }
            visited[state.vertex] = true;
            if (state.back) { // 帰りがけのとき
                continue;
            } else {
                for (Graph::Edge e : tree.edges[state.vertex]) {
                    if (visited[e.to]) {
                        continue;
                    }
                    stack.push_back(State{true, state.vertex, e.color, -e.distance});
                    stack.push_back(State{false, e.to, e.color, e.distance});
                }
            }
        }
    }
    vector<ll> dist_sum;
    struct ColorAccumInfo {
        size_t flat_tree_edge_idx;
        ll distance;
        int count;
    };
    unordered_map<int, vector<ColorAccumInfo>> color_accum_info;
    for (int idx = 0; idx < flat_tree.size(); idx++) {
        FlatTreeEdge edge = flat_tree[idx];
        ll now_dist = 0;
        if (dist_sum.size() > 0) {
            now_dist = dist_sum.back();
        }
        now_dist += edge.distance;
        dist_sum.push_back(now_dist);
        ColorAccumInfo now_accum_info{0, 0, 0};
        vector<ColorAccumInfo>& accum = color_accum_info[edge.color];
        if (accum.size() > 0) {
            now_accum_info = accum.back();
        }
        now_accum_info.flat_tree_edge_idx = idx;
        now_accum_info.distance += edge.distance;
        now_accum_info.count += (edge.distance >= 0) ? 1 : -1;
        accum.push_back(now_accum_info);
    }
    auto answer_query_from_root = [&flatten_index, &color_accum_info, &dist_sum](size_t vertex, int color, int modified_dist) -> ll {
        size_t flat_idx = flatten_index[vertex];
        vector<ColorAccumInfo>& accum_info = color_accum_info[color];
        auto mid = partition_point(accum_info.begin(), accum_info.end(), [flat_idx](const ColorAccumInfo& point) -> bool {
            return point.flat_tree_edge_idx < flat_idx;
        });
        size_t accum_info_len = mid - accum_info.begin();
        ll color_distance = 0;
        int color_count = 0;
        if (accum_info_len > 0) {
            color_distance = accum_info[accum_info_len-1].distance;
            color_count = accum_info[accum_info_len-1].count;
        }
        ll distance = 0;
        if (flat_idx > 0) {
            distance = dist_sum[flat_idx-1];
        }
        return distance - color_distance + modified_dist * color_count;
    };
    // LCA
    vector<size_t> parent_index;
    parent_index.resize(N, -1);
    vector<ll> depth;
    depth.resize(N, 0);
    {
        vector<bool> visited;
        visited.resize(N, false);
        vector<size_t> stack;
        stack.push_back(0);
        while (!stack.empty()) {
            size_t vertex = stack.back();
            stack.pop_back();
            visited[vertex] = true;
            for (Graph::Edge e : tree.edges[vertex]) {
                if (visited[e.to]) {
                    continue;
                }
                parent_index[e.to] = vertex;
                depth[e.to] = depth[vertex] + 1;
                stack.push_back(e.to);
            }
        }
    }
    vector<vector<size_t>> parent;
    vector<size_t> parent_component_init;
    parent_component_init.resize(N, -1);
    parent.resize((int)ceilf(log2(N)), parent_component_init);
    parent[0] = parent_index;
    for (int depth = 1; depth < parent.size(); depth++) {
        for (size_t vertex = 0; vertex < N; vertex++) {
            size_t half_parent = parent[depth-1][vertex];
            if (half_parent == -1) {
                parent[depth][vertex] = -1;
            } else {
                parent[depth][vertex] = parent[depth-1][half_parent];
            }
        }
    }
    auto lca = [&depth, &parent](size_t a, size_t b) -> size_t {
        if (depth[a] != depth[b]) {
            if (depth[a] < depth[b]) {
                swap(a, b);
            }
            size_t diff = depth[a] - depth[b];
            while (diff != 0) {
                int n = 0;
                while (true) {
                    if (diff < (1 << (n+1))) {
                        break;
                    }
                    n++;
                }
                assert(diff >= (1 << n));
                a = parent[n][a];
                diff = depth[a] - depth[b];
            }
        }
        while (true) {
            if (a == b) {
                return a;
            }
            if (parent[0][a] == parent[0][b]) {
                return parent[0][a];
            }
            int n = 0;
            while (true) {
                if (parent[n+1][a] == parent[n+1][b]) {
                    break;
                }
                n++;
            }
            a = parent[n][a];
            b = parent[n][b];
        }
    };
    for (int i = 0; i < Q; i++) {
        int x, y, u, v;
        cin >> x >> y >> u >> v;
        u--;
        v--;
        size_t a = lca(u, v);
        cout << answer_query_from_root(u, x, y) + answer_query_from_root(v, x, y) - 2 * answer_query_from_root(a, x, y) << endl;
    }
}

Measured memory_bytes: 40120