#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

struct Node {
    ll sum_salary;        // Sum of salaries in the segment
    ll sum_happiness;     // Sum of happiness in the segment
    ll uniform_salary;    // Uniform salary value if all salaries are the same
    bool is_uniform;      // Flag indicating if the segment is uniform
    ll lazy_set;          // Pending set operation (None if -1)
    ll lazy_add;          // Pending add operation

    Node() : sum_salary(0), sum_happiness(0), uniform_salary(0), is_uniform(true), lazy_set(-1), lazy_add(0) {}
};

const int MAXN = 200005;
int N, Q;
ll a[MAXN];
Node tree[4 * MAXN];

void build(int node, int l, int r) {
    if (l == r) {
        tree[node].sum_salary = a[l];
        tree[node].sum_happiness = 0;
        tree[node].uniform_salary = a[l];
        tree[node].is_uniform = true;
        tree[node].lazy_set = -1;
        tree[node].lazy_add = 0;
    } else {
        int mid = (l + r) / 2;
        build(2 * node, l, mid);
        build(2 * node + 1, mid + 1, r);
        tree[node].sum_salary = tree[2 * node].sum_salary + tree[2 * node + 1].sum_salary;
        tree[node].sum_happiness = tree[2 * node].sum_happiness + tree[2 * node + 1].sum_happiness;
        tree[node].is_uniform = tree[2 * node].is_uniform && tree[2 * node + 1].is_uniform && 
                                tree[2 * node].uniform_salary == tree[2 * node + 1].uniform_salary;
        if (tree[node].is_uniform) {
            tree[node].uniform_salary = tree[2 * node].uniform_salary;
        }
        tree[node].lazy_set = -1;
        tree[node].lazy_add = 0;
    }
}

void push(int node, int l, int r) {
    if (tree[node].lazy_set != -1) {
        // Apply the set operation
        ll c = tree[node].lazy_set;
        ll cnt = r - l + 1;
        if (tree[node].is_uniform) {
            ll s_prev = tree[node].uniform_salary;
            ll happiness_change = 0;
            if (c > s_prev) happiness_change = cnt;
            else if (c < s_prev) happiness_change = -cnt;
            tree[node].sum_happiness += happiness_change;
        } else {
            // Can't compute directly; need to propagate
            push(2 * node, l, (l + r) / 2);
            push(2 * node + 1, (l + r) / 2 + 1, r);
        }
        tree[node].sum_salary = c * cnt;
        tree[node].uniform_salary = c;
        tree[node].is_uniform = true;
        // Clear lazy tags
        tree[node].lazy_set = -1;
        tree[node].lazy_add = 0;
        if (l != r) {
            tree[2 * node].lazy_set = c;
            tree[2 * node].lazy_add = 0;
            tree[2 * node + 1].lazy_set = c;
            tree[2 * node + 1].lazy_add = 0;
        }
    }
    if (tree[node].lazy_add != 0) {
        ll c = tree[node].lazy_add;
        ll cnt = r - l + 1;
        if (tree[node].is_uniform) {
            ll s_prev = tree[node].uniform_salary;
            ll s_new = s_prev + c;
            ll happiness_change = 0;
            if (c > 0) happiness_change = cnt;
            else if (c < 0) happiness_change = -cnt;
            tree[node].sum_happiness += happiness_change;
            tree[node].uniform_salary = s_new;
        } else {
            // Can't compute directly; need to propagate
            push(2 * node, l, (l + r) / 2);
            push(2 * node + 1, (l + r) / 2 + 1, r);
        }
        tree[node].sum_salary += c * cnt;
        if (l != r) {
            if (tree[2 * node].lazy_set != -1) {
                tree[2 * node].lazy_set += c;
            } else {
                tree[2 * node].lazy_add += c;
            }
            if (tree[2 * node + 1].lazy_set != -1) {
                tree[2 * node + 1].lazy_set += c;
            } else {
                tree[2 * node + 1].lazy_add += c;
            }
        }
        tree[node].lazy_add = 0;
    }
}

void range_set(int node, int l, int r, int ql, int qr, ll c) {
    push(node, l, r);
    if (r < ql || l > qr) return;
    if (ql <= l && r <= qr) {
        tree[node].lazy_set = c;
        push(node, l, r);
    } else {
        int mid = (l + r) / 2;
        range_set(2 * node, l, mid, ql, qr, c);
        range_set(2 * node + 1, mid + 1, r, ql, qr, c);
        tree[node].sum_salary = tree[2 * node].sum_salary + tree[2 * node + 1].sum_salary;
        tree[node].sum_happiness = tree[2 * node].sum_happiness + tree[2 * node + 1].sum_happiness;
        tree[node].is_uniform = tree[2 * node].is_uniform && tree[2 * node + 1].is_uniform && 
                                tree[2 * node].uniform_salary == tree[2 * node + 1].uniform_salary;
        if (tree[node].is_uniform) {
            tree[node].uniform_salary = tree[2 * node].uniform_salary;
        } else {
            tree[node].uniform_salary = 0;
        }
    }
}

void range_add(int node, int l, int r, int ql, int qr, ll c) {
    push(node, l, r);
    if (r < ql || l > qr) return;
    if (ql <= l && r <= qr) {
        tree[node].lazy_add += c;
        push(node, l, r);
    } else {
        int mid = (l + r) / 2;
        range_add(2 * node, l, mid, ql, qr, c);
        range_add(2 * node + 1, mid + 1, r, ql, qr, c);
        tree[node].sum_salary = tree[2 * node].sum_salary + tree[2 * node + 1].sum_salary;
        tree[node].sum_happiness = tree[2 * node].sum_happiness + tree[2 * node + 1].sum_happiness;
        tree[node].is_uniform = tree[2 * node].is_uniform && tree[2 * node + 1].is_uniform && 
                                tree[2 * node].uniform_salary == tree[2 * node + 1].uniform_salary;
        if (tree[node].is_uniform) {
            tree[node].uniform_salary = tree[2 * node].uniform_salary;
        } else {
            tree[node].uniform_salary = 0;
        }
    }
}

pair<ll, ll> query_salary(int node, int l, int r, int ql, int qr) {
    push(node, l, r);
    if (r < ql || l > qr) return {0, 0};
    if (ql <= l && r <= qr) {
        return {tree[node].sum_salary, r - l + 1};
    } else {
        int mid = (l + r) / 2;
        auto left = query_salary(2 * node, l, mid, ql, qr);
        auto right = query_salary(2 * node + 1, mid + 1, r, ql, qr);
        return {left.first + right.first, left.second + right.second};
    }
}

pair<ll, ll> query_happiness(int node, int l, int r, int ql, int qr) {
    push(node, l, r);
    if (r < ql || l > qr) return {0, 0};
    if (ql <= l && r <= qr) {
        return {tree[node].sum_happiness, r - l + 1};
    } else {
        int mid = (l + r) / 2;
        auto left = query_happiness(2 * node, l, mid, ql, qr);
        auto right = query_happiness(2 * node + 1, mid + 1, r, ql, qr);
        return {left.first + right.first, left.second + right.second};
    }
}

ll gcd(ll a, ll b) {
    return b ? gcd(b, a % b) : a;
}

void print_fraction(ll num, ll denom) {
    ll g = gcd(abs(num), denom);
    num /= g;
    denom /= g;
    cout << num << "/" << denom << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> N >> Q;
    for (int i = 1; i <= N; ++i) {
        cin >> a[i];
    }
    build(1, 1, N);
    while (Q--) {
        int type;
        cin >> type;
        if (type == 0) {
            int l, r;
            ll c;
            cin >> l >> r >> c;
            range_set(1, 1, N, l, r, c);
        } else if (type == 1) {
            int l, r;
            ll c;
            cin >> l >> r >> c;
            range_add(1, 1, N, l, r, c);
        } else if (type == 2) {
            int l, r;
            cin >> l >> r;
            auto res = query_salary(1, 1, N, l, r);
            ll sum = res.first;
            ll cnt = res.second;
            print_fraction(sum, cnt);
        } else if (type == 3) {
            int l, r;
            cin >> l >> r;
            auto res = query_happiness(1, 1, N, l, r);
            ll sum = res.first;
            ll cnt = res.second;
            print_fraction(sum, cnt);
        }
    }
    return 0;
}
