#include <iostream>
#include <cmath>
#include <cstdio>
#include <cassert>
#include <string>
#include <set>
#include <vector>


using namespace std;
const int Nmax = 100100;

vector<int> primes;
int lp[Nmax * 10], A[Nmax];
set<int> actualPositions;
int N, M;

struct SegmentTree {
       int l,r;
       int value;
       SegmentTree *left, *right;
       SegmentTree(){};
       SegmentTree(SegmentTree *_left, SegmentTree * _right):left(_left), right(_right) {
                               if (!left && !right) return ;
                               l = left -> l;
                               r = right -> r;
                               value = max(left -> value, right -> value);
       }
};

SegmentTree * Build(int l, int r) {
            if (l == r) {
               SegmentTree *tree = new SegmentTree();
               tree -> l = tree -> r = l;
               tree -> left = tree -> right = NULL;
               tree -> value = lp[A[l]];
               return tree;
            }
            int mid = (l + r) >> 1;
            return new SegmentTree(Build(l, mid), Build(mid + 1, r));
}

int getMax(SegmentTree *tree, int l, int r) {
    if (l > r) return 1;
    if (tree -> l == l && tree -> r == r) {
             return tree -> value;
    }
    int mid = (tree -> l + tree -> r) >> 1;
    return max(getMax(tree -> left, l, min(r, mid)), 
               getMax(tree -> right, max(mid + 1, l), r));
}

void Update(SegmentTree *tree, int idx) {
     if (tree -> l == tree -> r) {
        A[idx] /= lp[A[idx]];
        tree -> value = lp[A[idx]];
        return ;         
     }
     int mid = (tree -> l + tree -> r) >> 1;
     if (idx <= mid)
        Update(tree -> left, idx);
     else
         Update(tree -> right, idx);
     tree -> value = max(tree -> left -> value, tree -> right -> value);
}

void Update(SegmentTree *tree, int l, int r) {
     set<int> :: iterator it = actualPositions.lower_bound(l);
     vector<int> toDelete;
     while (it != actualPositions.end() && *it <= r) {
           Update(tree, *it);
           if (A[*it] == 1)
              toDelete.push_back(*it);
           it ++;
     }
     for (int i = 0; i < toDelete.size(); i ++)
         actualPositions.erase(toDelete[i]);
}

inline void Solve() {
       scanf("%d %d", &N, &M);
       assert(1 <= N && N <= 100000);
       assert(1 <= M && M <= 100000);
       
       actualPositions.clear();
       for (int i = 1; i <= N; i ++) {
           scanf("%d", &A[i]);
           assert(1 <= A[i] && A[i] <= 1000000);
           if (A[i] > 1)
              actualPositions.insert(i);
       }
       SegmentTree *tree = Build(1,N);
       for (int it = 1; it <= M; it ++) {
           int type, l, r;
           scanf("%d %d %d", &type, &l, &r);
           assert(type == 0 || type == 1);
           assert(1 <= l && l <= r && r <= N);
           if (type == 0) {
                 Update(tree, l, r);
           }
           else {
                printf("%d ", getMax(tree, l, r));
           }
       }
       printf("\n");
}
       
int main() {
    lp[1] = 1;
    for (int i = 2; i < 10 * Nmax; i ++ ){
        if (lp[i] == 0) {
           primes.push_back(i);
           lp[i] = i;
        }   
        for (int j = 0; j < primes.size() && primes[j] <= lp[i] && i * 1ll * primes[j] < 10 * Nmax; j ++) {
            lp[primes[j] * i] = primes[j];
        }
    }
    
    int T;
    scanf("%d",&T);
    assert(1 <= T && T <= 100);
    while (T --> 0) {
          Solve();
    }
    
    return 0;
}