// 基本テンプレート
// #define _GLIBCXX_DEBUG // for STL debug
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <string>
#include <cstring>
#include <deque>
#include <list>
#include <queue>
#include <stack>
#include <vector>
#include <utility>
#include <algorithm>
#include <map>
#include <set>
#include <complex>
#include <cmath>
#include <limits>
#include <cfloat>
#include <climits>
#include <ctime>
#include <cassert>
#include <numeric>
#include <fstream>
#include <functional>
#include <bitset>
using namespace std;
 
#define rep(i,a,n) for(int (i)=(a); (i)<(n); (i)++)
#define repq(i,a,n) for(int (i)=(a); (i)<=(n); (i)++)
#define repr(i,a,n) for(int (i)=(a); (i)>=(n); (i)--)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define int long long int
 
template<typename T> void chmax(T &a, T b) {a = max(a, b);}
template<typename T> void chmin(T &a, T b) {a = min(a, b);}
template<typename T> void chadd(T &a, T b) {a = a + b;}
 
typedef pair<int, int> pii;
typedef long long ll;
 
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
const ll INF = 1001001001001001LL;
const ll MOD = 1000000007LL;

// 移動元と行先と辺のコストを記録する構造体
template <typename T>
struct Edge {
    int from, to;
    T cost;
    Edge(int s, T d) : to(s), cost(d) {}
    Edge(int f, int s, T d) : from(f), to(s), cost(d) {}

    bool operator<(const Edge &e) const {
        return cost < e.cost;
    }
    bool operator>(const Edge &e) const {
        return cost > e.cost;
    }
};

template <typename T>
using Graph = vector< vector< Edge<T> > >;

// 関節点を求める (artPoints)
// 橋を求める (bridges)
// 二重辺連結成分分解をする (BICC)

// 関節点は、取り除いたときに連結でなくなってしまうような頂点のこと
// 橋は、取り除いた時に連結でなくなってしまうような辺のこと

template <typename T>
struct graphLink {
    vector<int> ord, low, parent, cmp;
    vector< vector< Edge<T> > > G, H;

    // 橋の情報 (first < second となるように格納)
    vector< pair<int, int> > bridges;
    int cnt, v;

    // init
    graphLink(vector< vector< Edge<T> > > &S, int root=0) {
        int n = S.size();
        ord.resize(n, -1), low.resize(n, 0),
        parent.resize(n, -1), cmp.resize(n, -1);
        cnt = 0, v = n;
        G = S;
        dfs(root);
    }

    // 橋であるかの判定
    bool is_bridge(int x, int y) {
        if(ord[x] > ord[y]) swap(x, y);
        return ord[x] < low[y];
    }

    // dfs 木の作成と橋の列挙 (初期化と同時にやる)
    // usage: dfs(root);
    void dfs(int cur, int prev=-1) {
        low[cur] = cnt;
        ord[cur] = cnt++;

        for(auto x : G[cur]) {
            if(x.to == prev) continue;
            if(ord[x.to] < 0) {
                parent[x.to] = cur;
                dfs(x.to, cur);

                low[cur] = min(low[cur], low[x.to]);
            }
            else {
                low[cur] = min(low[cur], ord[x.to]);
            }
            if(is_bridge(cur, x.to)) {
                int a = min(cur, x.to);
                int b = max(cur, x.to);
                bridges.emplace_back(make_pair(a, b));
            }
        }
    }

    // 関節点を求める (root は dfs 木の root と一致させる)
    // root は子を 2 つ持っていれば関節点になる
    // それ以外の頂点に関しては ord[parent] <= low[i] のとき関節点になる
    // (lowlink でも親より深さが低い頂点にたどり着けないため)
    set<int> artPoints(int root) {
        set<int> se;
        int num = 0;
        for(int i=0; i<v; i++) {
            if(parent[i] < 0) continue;
            if(parent[i] == root) num++;
            else if(ord[parent[i]] <= low[i]) se.insert(parent[i]);
        }
        if(num >= 2) se.insert(0);
        return se;
    }

    // 二重辺連結成分分解 (橋となる辺を使わないように DFS)
    // Verified: AtCoder Regular Contest D: 旅行会社高橋君
    void BICC() {
        int k = 0;
        // point, number
        stack<pii> S;
        for(int i=0; i<v; i++) {
            if(cmp[i] >= 0) continue;
            cmp[i] = k;
            S.push(make_pair(i, k++));
            while(!S.empty()) {
                pii cur = S.top(); S.pop();
                for(auto x : G[cur.first]) {
                    if(cmp[x.to] >= 0) continue;
                    if(is_bridge(cur.first, x.to)) continue;
                    cmp[x.to] = cur.second;
                    S.push(make_pair(x.to, cmp[x.to]));
                }
            }
        }

        set< pair<int, int> > edge_set;
        H.resize(k);
        for(int i=0; i<v; i++) {
            for(auto x : G[i]) {
                int ca = cmp[i], cb = cmp[x.to];
                if(ca == cb) continue;
                if(ca > cb) swap(ca, cb);
                if(edge_set.count(make_pair(ca, cb))) continue;
                edge_set.insert(make_pair(ca, cb));
                H[ca].push_back(Edge<T>(cb, 1));
                H[cb].push_back(Edge<T>(ca, 1));
            }
        }
    }
};

const int HAPPY = 1;
const int SOSO  = 2;
const int SAD   = 3;

const int source = 0;
const int sink   = 1;

int N, M, u[100010], v[100010], w[100010], ans[100010];

vector<int> getMinDist(const Graph<int> &G, int s) {
    vector<int> res(N, INF);
    res[s] = 0;

    priority_queue< pair<int, int>, vector< pair<int, int> >, greater< pair<int, int> > > que;
    que.emplace(0, s);

    while(que.size()) {
        auto cur = que.top(); que.pop();
        int cost, pos; tie(cost, pos) = cur;

        if(res[pos] < cost) continue;
        for(auto e : G[pos]) {
            int to = e.to, dist = cost + e.cost;
            if(res[to] > dist) {
                res[to] = dist;
                que.emplace(dist, to);
            }
        }
    }
    return res;
}

signed main() {
    cin >> N >> M;
    fill(ans, ans + M, SOSO);

    Graph<int> G(N), revG(N);
    for(int i=0; i<M; i++) {
        cin >> u[i] >> v[i] >> w[i];
        u[i]--; v[i]--;
           G[ u[i] ].push_back(Edge<int>(v[i], w[i]));
        revG[ v[i] ].push_back(Edge<int>(u[i], w[i]));
    }

    vector<int> recG    = getMinDist(G   , source);
    vector<int> recrevG = getMinDist(revG, sink  );
    int origMin = recG[sink];

    Graph<int> H(N);
    map< pair<int, int>, int > edgecnt;
    map< pair<int, int>, int > edgeid;
    for(int i=0; i<M; i++) {
        int L = recG[ v[i] ], R = recrevG[ u[i] ];
        // ひっくり返すと最短路に使われるが長さ変わらない
        if(L + R + w[i] == origMin) {
            ans[i] = SOSO;
        }
        // ひっくり返すと最短路がさらに短くなる
        else if(L + R + w[i] < origMin) {
            ans[i] = HAPPY;
        }
        else {
            L = recG[ u[i] ], R = recrevG[ v[i] ];
            // fprintf(stderr, "#debug: u = %lld, v = %lld, weight = %lld, L = %lld, R = %lld\n", u[i], v[i], w[i], L, R);

            // そのままの状態で最短路に使われる
            if(L + R + w[i] == origMin) {
                int cnt = edgecnt[ make_pair(u[i], v[i]) ]++;
                if(cnt == 0) {
                    // fprintf(stderr, "edge: (%lld, %lld)\n", u[i], v[i]);
                    edgeid[ make_pair(u[i], v[i]) ] = i;
                    H[ u[i] ].push_back(Edge<int>(v[i], w[i]));
                    H[ v[i] ].push_back(Edge<int>(u[i], 0   ));
                }
            }
        }
    }

    graphLink<int> gl(H, source);
    for(int i=0; i<N; i++) {
        for(auto e : H[i]) {
            int x = i, y = e.to;
            if(e.cost == 0) continue;
            if(edgecnt[ make_pair(x, y) ] == 1) {
                if(gl.is_bridge(x, y)) {
                    int id = edgeid[ make_pair(x, y) ];
                    ans[id] = SAD;
                }
            }
        }
    }

    for(int i=0; i<M; i++) {
        if(ans[i] == SAD  ) cout << "SAD" << endl;
        if(ans[i] == SOSO ) cout << "SOSO" << endl;
        if(ans[i] == HAPPY) cout << "HAPPY" << endl;
    }
    return 0;
}



Measured memory_bytes: 58740