#include <bits/stdc++.h>
#ifdef Sabbir
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wsign-compare"
#endif
using namespace std;
#ifdef Sabbir
#include <E:\CodeBox\TempBox\Debug\debug.h>
#else
#define dbg(x...)
#endif
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
// Ordered set supporting order statistics.
#define ordered_set tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update>
#define ll long long
#define all(x) (x).begin(), (x).end()
#define in(v) for(auto &b: v) cin >> b;
#define pb push_back
// Sieve globals.
vector<int> primes;
const ll mx = 1e6+1;
vector<bool> isprime(mx, true);
// Sieve to generate primes up to mx.
void sieve(){
isprime[1] = false;
for (ll i = 2; i * i <= mx; i++){
if(isprime[i]){
for (ll j = i * i; j <= mx; j += i){
isprime[j] = false;
}
}
}
for (int i = 2; i < mx; i++){
if(isprime[i])
primes.pb(i);
}
}
void pagol(){
int n;
cin >> n;
vector<int> v(n);
in(v);
// We'll create groups indexed by their SPF.
// Since SPF(i) is either 1 (for i==1) or a prime (for i>=2), we allocate an array sized n+1.
vector<vector<int>> groups(n + 1);
// Process index 1 separately, as SPF(1) = 1.
groups[1].pb(1);
int mot = 0;
// For every number i from 2 to n, compute its smallest prime factor (SPF)
// by iterating over our precomputed primes.
for (int i = 2; i <= n; i++){
for (auto a : primes){
if (i % a == 0){
groups[a].pb(i);
mot = max(mot, a);
break;
}
}
}
// This will hold our final answer. (1-indexed)
vector<int> ans(n + 1, 0);
// Process group for SPF = 1 (i.e. index 1)
{
int spf = 1;
if(!groups[spf].empty()){
// Process indices in descending order.
reverse(all(groups[spf]));
ordered_set os;
for (auto idx : groups[spf]){
int required = v[idx - 1]; // required inversion count for index idx.
if (required > os.size()){
cout << "NO" << "\n";
return;
}
if (required == os.size()){
// When inversion count equals os.size(), we want A[idx] to be larger than every later element.
// Since we store negatives (so that a smaller negative means a larger A value),
// we need to insert a new negative value smaller than *os.begin().
if (os.size() > 0)
os.insert((*os.begin()) - 1); // Subtracting 1 gives a smaller (more negative) value.
else
os.insert(-10000000); // initial value if os is empty
ans[idx] = -(*os.rbegin());
} else {
ans[idx] = -(*os.find_by_order(required));
}
}
}
}
// Process groups for each prime factor.
for (auto a : primes){
if (a > mot) break;
if (groups[a].empty()) continue;
reverse(all(groups[a]));
ordered_set os;
for (auto idx : groups[a]){
int required = v[idx - 1];
if (required > os.size()){
cout << "NO" << "\n";
return;
}
if (required == os.size()){
if (os.size() > 0)
os.insert((*os.begin()) - 1); // Correct: subtract 1 to get a new value smaller than the current minimum.
else
os.insert(-10000000);
ans[idx] = -(*os.rbegin());
} else {
ans[idx] = -(*os.find_by_order(required));
}
}
}
cout << "YES" << "\n";
for (int i = 1; i <= n; i++){
cout << ans[i] << " ";
}
cout << "\n";
}
int32_t main() {
ios::sync_with_stdio(0);
cin.tie(0);
sieve();
int t;
cin >> t;
while (t--){
pagol();
}
return 0;
}