TProcura
Biblioteca em C++ para testes paramétricos de algoritmos, e coleção de algoritmos de procura e otimização
Loading...
Searching...
No Matches
TProcura.cpp
Go to the documentation of this file.
1#include "TProcura.h"
2#include <stdio.h>
3#include <time.h>
4#include <string.h>
5#ifdef MPI_ATIVO
6#include <mpi.h>
7#endif
8constexpr int BUFFER_SIZE = 1024;
9
10// Resultado retornado pelo algoritmo na última execução.
12// tempo consumido na última execução.
13double TProcura::tempo = 0;
14// numero de iterações, conforme definido no algoritmo
16
17// deadline da corrida atual
18clock_t TProcura::instanteFinal = 0;
19// flag de problemas de memória esgotada
20bool TProcura::memoriaEsgotada = false;
21// ID da instância atual (problemas com várias instâncias, a utilizar em SolucaoVazia())
22TParametro TProcura::instancia = { "",1,1,1 };
23// nome do ficheiro de uma instância (utilizar como prefixo, concatenando com ID da instância)
25// idêntico mas para gravar a instância (terá sido gerada)
27
28
29// adicionar parâmetros específicos, se necessário
31// adicionar indicadores conforme a necessidade
33// lista por ordem dos indicadores a utilizar
35
36// MPI - rank do processo
37int TProcura::mpiID = 0;
38// MPI - número de processos
39int TProcura::mpiCount = 1;
40// Modo MPI : 0 = divisão estática, 1 = mestre-escravo
41int TProcura::modoMPI = 0;
42// Gravar solução CSV (todas as ações): 0 = não grava, 1 = grava
45int TProcura::modoKMGT = 0;
46
47
48// conjuntos de valores de parâmetros, para teste
50
52{
53 // definir parametros base
54 parametro = {
55 { "ALGORITMO", 1, 1, 1, "Algoritmo base a executar.", {"Algoritmo base"} },
56 { "NIVEL_DEBUG", 0, 0, 4, "Nível de debug, de reduzido a completo.",
57 { "NADA", "ATIVIDADE", "PASSOS", "DETALHE", "COMPLETO" } },
58 { "SEMENTE", 1, 1, 1000000000, "Semente aleatória para inicializar a sequência de números pseudo-aleatórios." },
59 { "LIMITE_TEMPO", 10, 1, 3600, "Limite de tempo em segundos. " },
60 { "LIMITE_ITERACOES", 0, 0, 1000000000, "Limite de número de iterações (0 não há limite). " }
61 };
62
63 // definir indicadores base
64 indicador = {
65 { "Resultado", "Resultado do algoritmo, interpretado conforme o algoritmo (sucesso/insucesso, custo, qualidade, valor, etc.).", IND_RESULTADO },
66 { "Tempo(ms)", "Tempo em milissegundos da execução (medida de esforço computacional).", IND_TEMPO },
67 { "Iterações", "Iterações do algoritmo, interpretadas conforme o algoritmo (medida de esforço independente do hardware).", IND_ITERACOES }
68 };
70
71 // colocar as configurações vazias (podem ser inicializadas se existirem configurações por omissão)
72 configuracoes = {};
73}
74
75// retorna o valor do indicador[id]
76int64_t TProcura::Indicador(int id) {
77 switch (id) {
78 case IND_RESULTADO:
79 return resultado;
80 case IND_TEMPO:
81 return (int64_t)(1000 * tempo + 0.5);
82 case IND_ITERACOES:
83 return iteracoes;
84 }
85 return 0;
86}
87
88// Escrever informacao de debug sobre o objeto atual
89void TProcura::Debug(bool completo)
90{
91 Debug(ATIVIDADE, false, "\nTProcura::Debug() método não redefinido.");
92}
93
94// Chamar antes de iniciar uma procura
103
104// Metodo para teste manual do objeto (chamadas aos algoritmos, construcao de uma solucao manual)
105// Este metodo destina-se a testes preliminares, e deve ser redefinido apenas se forem definidos novos algoritmos
107{
108 int selecao;
111 Inicializar();
113 while (true) {
114 printf("\n%s", *nome);
116 Debug();
118 printf("\n"
119 "┌─ %-2sMenu ─────────┬────────────────┬─────────────────────┬──────────────┐\n"
120 "│ 1 %-2s " COR_LEVE "Instância" COR_RESET " │ 2 %-2s " COR_LEVE "Explorar" COR_RESET " │ 3 %-2s " COR_LEVE "Parâmetros" COR_RESET " │ 4 %-2s " COR_LEVE "Solução" COR_RESET " │\n"
121 "│ 5 %-2s " COR_LEVE "Indicadores" COR_RESET " │ 6 %-2s " COR_LEVE "Executar" COR_RESET " │ 7 %-2s " COR_LEVE "Configurações" COR_RESET " │ 8 %-2s " COR_LEVE "Teste" COR_RESET " │\n"
122 "└───────────────────┴────────────────┴─────────────────────┴──────────────┘",
123 Icon(EIcon::MENU), Icon(EIcon::INST), Icon(EIcon::EXP), Icon(EIcon::PARAM),
124 Icon(EIcon::SOL), Icon(EIcon::IND), Icon(EIcon::EXEC), Icon(EIcon::CONF),
125 Icon(EIcon::TESTE));
126 if ((selecao = NovoValor("\nOpção: ")) == NAO_LIDO)
127 return;
128 switch (Dominio(selecao, 0, 9)) {
129 case 0: return;
130 case 1: SolicitaInstancia(); break;
131 case 2: Explorar(); break;
132 case 3: EditarParametros(); break;
133 case 4: MostrarSolucao(); break;
134 case 5: if (EditarIndicadores())
135 resultados = {};
136 break;
137 case 6:
138 // executar um algoritmo
139 printf("\n═╤═ %-2s Execução iniciada ═══", Icon(EIcon::EXEC));
146 printf("\n═╧═ %-2s Execução terminada %-2s %s ═══",
147 Icon(EIcon::FIM), Icon(EIcon::TEMPO),
149 break;
150 case 7: EditarConfiguracoes(); break;
151 case 8: {
153 TesteEmpirico(instancias, NovoTexto("🗎 Ficheiro resultados (nada para mostrar no ecrã): "));
154 break;
155 }
156 case 9: return;
157 default: Mensagem(Icon(EIcon::IMP), "Opção não definida."); break;
158 }
159 }
160}
161
162// Idêntico ao teste empírico, mas utiliza a configuração atual e verifica se uma solução é válida para cada instância
163void TProcura::TesteValidacao(TVector<int> instancias, TVector<int> impossiveis, TVector<int> referencias, TString fichSolucoes, TString fichResultados)
164{
165 TVector<TResultado> solucoes; // guarda soluções para valiação
166 TVector<TResultado> resultados; // guarda resultados da validação para gravação
167 TVector<int> atual;
168 TVector<TVector<int>> instSolucoes; // para cada instância, os IDs dos resultados
170
171 TesteInicio(instancias, atual);
172#ifdef VPL_ATIVO
173 Debug(ATIVIDADE, false, "\n<|--\n");
174#endif
175 instSolucoes.Count(instancias.Count());
176
177 // ler ficheiro de soluções, formato: id; (qualquer número de parâmetros); solução
178 for (auto& linha : TString().printf("%s.csv", *fichSolucoes).readLines()) {
179 TVector<TString> tokens = linha.tok(";");
180 int id = atoi(tokens.First());
181 int indice = instancias.Find(id);
182 if (linha[linha.Count() - 2] == ';')
183 tokens += TString("vazio");
184 if (tokens.Count() < 3 || indice < 0)
185 continue;
186 instSolucoes[indice] += solucoes.Count(); // registar o índice do resultado para a instância correspondente
187 int tempo = atoi(tokens[tokens.Count() - 2]);
188 solucoes += { id, 0, { tempo }, tokens.Last().tok()}; // registar a instância, solução e indicadores (a preencher após validação)
189 }
190
191 Debug(ATIVIDADE, false,
192 "\n ├─ %-2sSoluções:%d %-2sInstâncias: %d.",
193 Icon(EIcon::SUCESSO), solucoes.Count(),
194 Icon(EIcon::INST), instancias.Count()) &&
195 fflush(stdout);
196
197 // verificar cada instância, para as soluções existentes
198 for (auto inst : instancias) {
199 int validas = 0, invalidas = 0, melhor = RES_VAZIO, pior = RES_VAZIO, tempo = 0;
200 int indice = instancias.Find(inst);
201 bool impossivel = impossiveis.Find(inst) >= 0;
202 if (indice >= 0)
203 for (auto solucao : instSolucoes[indice]) {
204 tempo += (int)solucoes[solucao].valor.First(); // acumular tempo das soluções para a instância atual
205 if (impossivel) {
206 // se a instância é conhecida por ser impossível, a solução tem de ser vazia e o tempo dentro do permitido
207 if (solucoes[solucao].solucao.First() == TString("vazio"))
208 {
209 validas++;
211 if (pior == RES_VAZIO)
213 Debug(COMPLETO, false,
214 "\n ├─ %-2s:%d %-2s %-2s %-2s %-2s %d",
215 Icon(EIcon::INST), inst,
216 Icon(EIcon::SUCESSO),
217 Icon(EIcon::VALOR), Icon(EIcon::IMP),
218 Icon(EIcon::TEMPO), tempo);
219 }
220 else {
221 // se existe solução para uma instância impossível, é inválida
222 invalidas++;
223 if (melhor == RES_VAZIO)
226 Debug(COMPLETO, false,
227 "\n ├─ %-2s:%d %-2s %-2s %d",
228 Icon(EIcon::INST), inst,
229 Icon(EIcon::INSUC),
230 Icon(EIcon::TEMPO), tempo);
231 for (auto& token : solucoes[solucao].solucao)
232 Debug(COMPLETO, false, " %s", *token);
233 }
234 }
235 else {
236 // validar solução para a instância atual, e calcular indicadores
237 // gravar o ID da instância atual
238 instancia.valor = inst;
239 Inicializar();
241 // validar a solução para a instância atual
242 if (Validar(solucoes[solucao].solucao)) {
243 validas++;
245 if (resultado >= 0) {
246 if (melhor == RES_VAZIO || melhor > resultado)
248 if (pior == RES_VAZIO || (pior >= 0 && pior < resultado))
249 pior = resultado;
250 }
251 Debug(COMPLETO, false,
252 "\n ├─ %-2s:%d %-2s %-2s %d %-2s %d",
253 Icon(EIcon::INST), inst,
254 Icon(EIcon::SUCESSO),
255 Icon(EIcon::VALOR), resultado,
256 Icon(EIcon::TEMPO), tempo);
257 }
258 else {
259 invalidas++;
260 if (melhor == RES_VAZIO)
263 Debug(COMPLETO, false,
264 "\n ├─ %-2s:%d %-2s %-2s %d",
265 Icon(EIcon::INST), inst,
266 Icon(EIcon::INSUC),
267 Icon(EIcon::TEMPO), tempo);
268 }
269 }
270 }
271 if (validas + invalidas == 0) {
272 // se não existem soluções para a instância, considerar como não resolvida
273 invalidas++;
275 }
276 // registar a instância e resultados (válidas, inválidas, melhor e pior valores), e tempo para gravação
277 resultados += { inst, 0, { validas, invalidas, melhor, pior, tempo }, { }};
278 }
279
282
283 // gravar resultados, um por instância
285
286 // repor a instância atual
288 Inicializar();
289#ifdef VPL_ATIVO
290 Debug(ATIVIDADE, false, "\n--|>\n");
291#endif
292 TesteFim();
293
294}
295
297 int validas = 0, melhorCusto = 0, piorCusto = 0, tempoTotal = 0;
298 double taxaEficacia = 0, taxaQualidade = 0, taxaEficiencia = 0, desempenho = 0;
299 bool considerarQualidade = (referencias[1] > referencias[0]); // custoMin < custoMax (se iguais, o custo não é considerado para o indicador global)
300 for (auto& res : resultados) {
301 // res.valor = { validas, invalidas, melhor, pior, tempo }
302 // instância válida apenas se todas as soluções para a instância forem válidas
303 if (res.valor[0] > 0 && res.valor[1] == 0) {
304 validas++;
305 if (res.valor[2] >= 0)
306 melhorCusto += (int)res.valor[2];
307 if (res.valor[3] >= 0)
308 piorCusto += (int)res.valor[3];
309 tempoTotal += (int)res.valor[4];
310 }
311 // não resolvidas se existirem resultados inválidos
312 if (res.valor[1] > 0) {
313 // custo máximo médio por instância (a dobrar)
314 piorCusto += 2 * referencias[1] / resultados.Count();
315 // tempo máximo por instância
316 tempoTotal += referencias[3] / resultados.Count();
317 }
318 }
319 Debug(ATIVIDADE, false,
320 "\n ├─ %-2sVálidas:%d %-2sInstâncias: %d.",
321 Icon(EIcon::SUCESSO), validas,
322 Icon(EIcon::INST), resultados.Count()) &&
323 fflush(stdout);
324 Debug(ATIVIDADE, false,
325 "\n ├─ %-2sMelhor:%d %-2sPior: %d.",
326 Icon(EIcon::VALOR), melhorCusto,
327 Icon(EIcon::VALOR), piorCusto) &&
328 fflush(stdout);
329 Debug(ATIVIDADE, false,
330 "\n ├─ %-2sTempo(ms):%d.",
331 Icon(EIcon::TEMPO), tempoTotal) &&
332 fflush(stdout);
333
334 // indicador de desempenho global:
335 // - taxaEficacia * taxaQualidade * taxaEficiencia (todos entre 0 e 1)
336 // - resultado final na escala entre 0 e 100 pontos.
337 // eficácia: percentagem de instâncias resolvidas
338 taxaEficacia = 100.0 * validas / resultados.Count();
339 // qualidade: (custoMax - custoTotal) / (custoMax - custoMin)
341 taxaQualidade = 100.0 * (referencias[1] - piorCusto) / (referencias[1] - referencias[0]);
342 // eficiência: (tempoMax - tempoTotal) / (tempoMax - tempoMin)
343 taxaEficiencia = 100.0 * (referencias[3] - tempoTotal) / (referencias[3] - referencias[2]);
344 // ajuste de limites
346 taxaQualidade = (taxaQualidade > 100 ? 100 : (taxaQualidade < 0 ? 0 : taxaQualidade));
347 taxaEficiencia = (taxaEficiencia > 100 ? 100 : (taxaEficiencia < 0 ? 0 : taxaEficiencia));
348 // calculo do indicador global
351 Debug(ATIVIDADE, false,
352 "\n ├─ %-2s %.1f%% (%-2s %.1f%% %-2s %.1f%% %-2s %.1f%%)",
353 Icon(EIcon::IND), desempenho,
354 Icon(EIcon::SUCESSO), taxaEficacia,
355 Icon(EIcon::VALOR), taxaQualidade,
356 Icon(EIcon::TEMPO), taxaEficiencia);
357 else
358 Debug(ATIVIDADE, false,
359 "\n ├─ %-2s %.1f%% (%-2s %.1f%% %-2s %.1f%%)",
360 Icon(EIcon::IND), desempenho,
361 Icon(EIcon::SUCESSO), taxaEficacia,
362 Icon(EIcon::TEMPO), taxaEficiencia);
363 fflush(stdout);
364
365#ifdef VPL_ATIVO
366 // nota no VPL
367 Debug(ATIVIDADE, false, "\n--|>\nGrade :=>> %d\n<|--\n", (int)(desempenho + 0.5));
368#endif
369}
370
371
372void TProcura::MostraCaixa(TVector<TString> titulo, ECaixaParte parte, TVector<int> largura, bool aberta, int identacao) {
373 for (int i = 0; i < titulo.Count(); i++) {
374 unsigned int len = (unsigned int)(
376 largura[i] :
378
379 if (len > 100)
380 len = 0;
381
382 switch (parte) {
384
385 if (i == 0) {
386 if (!titulo[i].Empty())
387 printf("\n%*s┌─ %s ─", identacao, "", *titulo[i]);
388 else
389 printf("\n%*s┌────", identacao, "");
390 break;
391 }
392 if (!titulo[i].Empty())
393 printf("┬─ %s ─", *titulo[i]);
394 else
395 printf("┬────");
396 break;
398 if (i == 0) {
399 if (!titulo[i].Empty())
400 printf("\n%*s├─ %s ─", identacao, "", *titulo[i]);
401 else
402 printf("\n%*s├────", identacao, "");
403 break;
404 }
405 if (!titulo[i].Empty())
406 printf("┼─ %s ─", *titulo[i]);
407 else
408 printf("┼────");
409 break;
411 if (i == 0) { printf("\n%*s│ %s", identacao, "", *titulo[i]); break; }
412 printf("│ %s", *titulo[i]); break;
414 if (i == 0) { printf("\n%*s└", identacao, ""); break; }
415 printf("┴"); break;
416 }
417
418 // mostrar a barra com len de comprimento
419 while (len-- > 0)
420 printf(parte == ECaixaParte::Meio ? " " : "─");
421
422 }
423
424 if (!aberta)
425 switch (parte) {
426 case ECaixaParte::Topo: printf("┐"); break;
427 case ECaixaParte::Separador: printf("┤"); break;
428 case ECaixaParte::Meio: printf("│"); break;
429 case ECaixaParte::Fundo: printf("┘"); break;
430 }
431}
432
433
434void TProcura::MostraCaixa(TString titulo, ECaixaParte parte, int largura,
435 bool aberta, int identacao, const char* icon)
436{
437 // início da caixa ou linha de separação ou fim da caixa
438 bool novaLinha = true;
439 if (identacao < 0) {
440 novaLinha = false;
441 identacao = -identacao - 1;
442 }
443 unsigned int len = (unsigned int)(
445 largura - (titulo.Empty() ? 0 : compat::ContaUTF8(titulo) - 4) :
447
448 if (icon[0] != 0)
449 len -= 3;
450
451 if (len > 100)
452 len = 0;
453
454 if (novaLinha)
455 printf("\n");
456 switch (parte) {
458 if (icon[0] != 0)
459 printf("%*s┌─ %-2s%s ─", identacao, "", icon, *titulo);
460 else
461 printf("%*s┌─ %s ─", identacao, "", *titulo);
462 break;
464 if (icon[0] != 0)
465 printf("%*s├─ %-2s%s ─", identacao, "", icon, *titulo);
466 else
467 printf("%*s├─ %s ─", identacao, "", *titulo);
468 break;
470 if (icon[0] != 0)
471 printf("%*s│ %-2s%s", identacao, "", icon, *titulo);
472 else
473 printf("%*s│ %s", identacao, "", *titulo);
474 break;
476 printf("%*s└", identacao, "");
477 if (!titulo.Empty()) { // texto a ser inserido no fundo
478 if (icon[0] != 0)
479 printf("─ %-2s%s ─", icon, *titulo);
480 else
481 printf("─ %s ─", *titulo);
482 }
483 break;
484 }
485
486 // mostrar a barra com len de comprimento
487 while (len-- > 0)
488 printf(parte == ECaixaParte::Meio ? " " : "─");
489
490 if (!aberta)
491 switch (parte) {
492 case ECaixaParte::Topo: printf("┐"); break;
493 case ECaixaParte::Separador: printf("┤"); break;
494 case ECaixaParte::Meio: printf("│"); break;
495 case ECaixaParte::Fundo: printf("┘"); break;
496 }
497}
498
499void TProcura::MostraCaixa(TVector<TString> textos, int largura, bool aberta, int identacao) {
501 for (int i = 1; i < textos.Count(); i++)
504}
505
506void TProcura::Mensagem(TString titulo, const char* fmt, ...) {
507 if (titulo.Empty())
508 titulo = "⚠️";
509
511 va_start(args, fmt);
514
515 int64_t len = vsnprintf(nullptr, 0, fmt, args_copy);
517
518 TVector<char> texto((int)len + 1);
519 if (texto.Data()) {
520 vsnprintf(texto.Data(), len + 1, fmt, args);
521 len = compat::ContaUTF8(texto.Data()) + 2;
523 MostraCaixa(textos, len < 20 ? 20 : (int)len);
524 }
525 va_end(args);
526}
527
529void TProcura::DebugHSL(float h, float s, float l, bool fundo) {
530 if (h < 0 || h > 360) { // reset de cores
531 printf("%s", COR_RESET);
532 }
533 else {
534 float f = (2 * l - 1);
535 float c = (1 - (f < 0 ? -f : f)) * s;
536
537 float h60 = h / 60.0f;
538 float hmod2 = h60 - 2 * int(h60 / 2);
539 float x = c * (1 - ((hmod2 - 1) < 0 ? -(hmod2 - 1) : (hmod2 - 1)));
540 float m = l - c / 2;
541
542 float r, g, b;
543 if (h < 60) { r = c; g = x; b = 0; }
544 else if (h < 120) { r = x; g = c; b = 0; }
545 else if (h < 180) { r = 0; g = c; b = x; }
546 else if (h < 240) { r = 0; g = x; b = c; }
547 else if (h < 300) { r = x; g = 0; b = c; }
548 else { r = c; g = 0; b = x; }
549
550 printf("\x1b[%d;2;%d;%d;%dm", (fundo ? 48 : 38),
551 (int)((r + m) * 255), (int)((g + m) * 255), (int)((b + m) * 255));
552 }
553}
554
555
556
557void TProcura::MostraParametros(int detalhe, TVector<int>* idParametros, TString titulo) {
558 int nElementos = (idParametros == NULL ? parametro.Count() : idParametros->Count());
559 int col = 2;
560 bool parBin = false;
561 if (titulo.Empty())
562 titulo = "Parâmetros";
563 // detalhe 0 é só uma linha (separador)
564 if (detalhe) {
565 MostraCaixa(titulo, ECaixaParte::Topo, 70, true, 0, Icon(EIcon::PARAM));
567 }
568 else {
570 printf(" ");
571 }
572 col = 3;
573
574 for (int i = 0; i < nElementos; i++) {
575 int parID = (idParametros == NULL ? i : (*idParametros)[i]);
576 if (!ParametroAtivo(parID))
577 continue;
578 // caso o parâmetro seja 0/1 mostrar o valor em cor 0=vermelho 1=verde
579 if ((parBin = (parametro[parID].min == 0 && parametro[parID].max == 1)) == true) {
580 // identificação do parâmetro e valor com cor
581 if (detalhe == 0 || parametro[parID].nome.Empty() ||
582 (detalhe == 1 && !parametro[parID].dependencia.Empty()))
583 col += printf("%sP%d%s%s" COR_RESET,
584 (const char*)(Parametro(parID) == 1 ? COR_ATIVO_LEVE : COR_INATIVO_LEVE),
585 parID + 1,
586 (const char*)(Parametro(parID) == 1 ? COR_ATIVO : COR_INATIVO),
587 Icon(Parametro(parID) == 1 ? EIcon::SEL : EIcon::NSEL)) - COR_LEVE_TAM;
588 else {
589 if (detalhe == 2 && !parametro[parID].dependencia.Empty())
590 col += printf(" ");
591 col += printf("%sP%d(%s)%s%s" COR_RESET,
592 (const char*)(Parametro(parID) == 1 ? COR_ATIVO_LEVE : COR_INATIVO_LEVE),
593 parID + 1, *parametro[parID].nome,
594 (const char*)(Parametro(parID) == 1 ? COR_ATIVO : COR_INATIVO),
595 Icon(Parametro(parID) == 1 ? EIcon::SEL : EIcon::NSEL)) - COR_LEVE_TAM;
596 }
597 }
598 else {
599 // identificação do parâmetro
600 if (detalhe == 0 || parametro[parID].nome.Empty() ||
601 (detalhe == 1 && !parametro[parID].dependencia.Empty()))
602 col += printf(COR_LEVE "P%d=" COR_RESET, parID + 1) - COR_LEVE_TAM;
603 else {
604 if (detalhe == 2 && !parametro[parID].dependencia.Empty())
605 col += printf(" ");
606 col += printf(COR_LEVE "P%d(%s):" COR_RESET " ", parID + 1, *parametro[parID].nome) - COR_LEVE_TAM;
607 }
608 // valor do parâmetro
609 if (detalhe > 1 && col < 30)
610 col += printf("%*s", (30 - col), "");
611 if (detalhe == 0 || parametro[parID].nomeValores.Empty() ||
612 (detalhe == 1 && !parametro[parID].dependencia.Empty()))
613 col += printf("%s", *MostraInt(Parametro(parID)));
614 else
615 col += printf("%s", *parametro[parID].nomeValores[Parametro(parID) - parametro[parID].min]);
616 }
617
618 // mostrar intervalo permitido
619 if (detalhe > 1) {
620 if (col < 40)
621 col += printf("%*s", (40 - col), "");
622 col += printf(" " COR_LEVE "(%s a %s)" COR_RESET,
623 *MostraInt(parametro[parID].min, false),
624 *MostraInt(parametro[parID].max, false)) - COR_LEVE_TAM;
625 }
626 if (detalhe == 2 && !parametro[parID].dependencia.Empty()) {
627 // mostrar variável dependente
628 int dependente = parametro[parID].dependencia.First();
629 col += printf(COR_LEVE " [P%d(%s)]" COR_RESET " ", dependente + 1, *parametro[dependente].nome) - COR_LEVE_TAM;
630 }
631 // separador/mudança de linha
632 if (i < nElementos - 1) {
633 if (detalhe > 1 || col > 70) { // limite de largura
634 if (detalhe == 0) {
635 MostraCaixa(" ", ECaixaParte::Separador, 1, true, 1, Icon(EIcon::PARAM));
636 printf(" ");
637 }
638 else
640 col = 3;
641 }
642 else if (detalhe > 0)
643 col += printf(" | ");
644 else
645 col += printf(" ");
646 }
647 }
648 if (detalhe)
650}
651
653 int opcao = 0;
654 bool editado = false;
655 while (true) {
657 do {
658 if (opcao < 0) {
659 opcao = -opcao;
660 opcao = Dominio(opcao, 1, indicador.Count());
661 printf(COR_LEVE "I%d(%s):" COR_RESET " %d", opcao, *indicador[opcao - 1].nome, (int)Indicador(opcao - 1));
662 }
663 if ((opcao = NovoValor("\nIndicador (positivo ativa/desativa; negativo calcula): ")) == NAO_LIDO || opcao == 0)
664 return editado;
665 } while (opcao < 0);
666 opcao = Dominio(opcao, 1, indicador.Count());
667 if (indicador[opcao - 1].indice >= 0) {
668 for (int i = 0; i < indicador.Count(); i++)
669 if (i != opcao - 1 && indicador[i].indice > indicador[opcao - 1].indice)
670 indicador[i].indice--;
671 indicador[opcao - 1].indice = -1;
672 indAtivo -= (opcao - 1);
673 }
674 else {
675 indicador[opcao - 1].indice = 0;
676 for (int i = 0; i < indicador.Count(); i++)
677 if (i != opcao - 1 && indicador[i].indice >= indicador[opcao - 1].indice)
678 indicador[opcao - 1].indice = indicador[i].indice + 1;
679 indAtivo += (opcao - 1); // coloca no fim
680 }
681 // invalidar resultados atuais
682 editado = true;
683 }
684 return editado;
685}
686
688 int opcao = 0, valor;
689 while (true) {
691 if ((opcao = NovoValor("\nParâmetro:")) == NAO_LIDO || opcao == 0)
692 return;
693 opcao = Dominio(opcao, 1, parametro.Count());
694 if (!ParametroAtivo(opcao - 1)) {
695 printf("\nParâmetro inativo.");
696 continue;
697 }
698 // iniciar caixa com nome do parametro
700 TString().printf("%-2s P%d(%s)", Icon(EIcon::PARAM), opcao, *parametro[opcao - 1].nome),
702 // mostrar descrição se existir
703 if (!parametro[opcao - 1].descricao.Empty())
705 // mostrar textos dos valores possíveis, caso existam
706 if (!parametro[opcao - 1].nomeValores.Empty())
707 for (int i = parametro[opcao - 1].min; i <= parametro[opcao - 1].max; i++) {
709 printf(COR_LEVE "%d:" COR_RESET " %s", i,
710 *parametro[opcao - 1].nomeValores[i - parametro[opcao - 1].min]);
711 }
712 else {
713 // mostrar intervalo possível
715 printf("Intervalo: %s a %s",
716 *MostraInt(parametro[opcao - 1].min),
717 *MostraInt(parametro[opcao - 1].max));
718 }
720
721 // valor atual
722 if (!parametro[opcao - 1].nome.Empty())
723 printf("\n%s (atual %d): ", *parametro[opcao - 1].nome, Parametro(opcao - 1));
724 else
725 printf("\nP%d (atual %d): ", opcao, Parametro(opcao - 1));
726 // solicitar valor
727 valor = NovoValor("");
728 if (valor != NAO_LIDO || valor == 0)
729 Parametro(opcao - 1) =
730 Dominio(valor,
731 parametro[opcao - 1].min,
732 parametro[opcao - 1].max);
733 }
734}
735
737{
738 int id = -1;
739 // procurar pela configuração
740 for (int i = 0; i < configuracoes.Count() && id < 0; i++)
741 if (configuracoes[i].Equal(parametros))
742 id = i;
743 if (id < 0) {
746 return configuracoes.Count() - 1;
747 }
748 return id;
749}
750
751
752// gravar (ou ler) a configuração atual
753void TProcura::ConfiguracaoAtual(TVector<int>& parametros, int operacao) {
754 if (operacao == GRAVAR) {
755 for (int i = 0; i < parametro.Count() && i < parametros.Count(); i++)
757 }
758 else if (operacao == LER) {
759 parametros = {};
760 for (int i = 0; i < parametro.Count(); i++)
762 }
763}
764
766{
767 static const int64_t segundo = 1000;
768 static const int64_t minuto = 60 * segundo;
769 static const int64_t hora = 60 * minuto;
770 static const int64_t dia = 24 * hora;
771 static const int64_t semana = 7 * dia;
772 static const int64_t mes = 30 * dia;
773 static const int64_t ano = 365 * dia;
775 static TVector<char> unidadesStr = { 'a', 'm', 's', 'd', 'h', '\'', '"' };
776 TString str;
777
778 int64_t ms = (int64_t)(1000 * segundos + 0.5);
779
780 for (int i = 0; i < unidades.Count(); i++)
781 if (ms >= unidades[i]) {
782 str.printf("%" PRId64 "%c ", ms / unidades[i], unidadesStr[i]);
783 ms %= unidades[i];
784 }
785 if (ms > 0)
786 str.printf("%" PRId64 "ms ", ms);
787
788 return str;
789}
790
792TString TProcura::MostraInt(int64_t valor, bool cor) {
793 static const int64_t kilo = 1000; // K - kilo
794 static const int64_t mega = 1000 * kilo; // M - mega
795 static const int64_t giga = 1000 * mega; // G - giga
796 static const int64_t tera = 1000 * giga; // T - tera
797 static TVector<int64_t> unidades = { tera, giga, mega, kilo };
798 static TVector<char> unidadesStr = { 'T', 'G', 'M', 'K' };
799 TString str;
800 if (valor == 0)
801 return TString("0");
802 if (valor < 0) {
803 str += "-";
804 valor = -valor;
805 }
806 if (modoKMGT)
807 for (int i = 0; i < unidades.Count(); i++)
808 if (valor >= unidades[i]) {
809 if (cor)
810 str.printf("%" PRId64 COR_LEVE "%c" COR_RESET, valor / unidades[i], unidadesStr[i]);
811 else
812 str.printf("%" PRId64 "%c", valor / unidades[i], unidadesStr[i]);
813 valor %= unidades[i];
814 }
815 if (valor > 0)
816 str.printf("%" PRId64, valor);
817 return str;
818}
819
820
821void TProcura::InserirRegisto(TVector<TResultado>& resultados, int inst, int conf)
822{
823 resultados += { inst, conf };
824 for (auto ind : indAtivo)
825 Registo(resultados.Last(), ind, Indicador(ind));
826 // adicionar no final a solução codificada em inteiros
827 resultados.Last().valor += CodificarSolucao();
828 resultados.Last().solucao = Solucao();
829}
830
831int64_t TProcura::Registo(TResultado& resultado, int id)
832{
833 if (id >= 0 && id < indicador.Count() && indicador[id].indice >= 0)
834 return resultado.valor[indicador[id].indice];
835 return 0;
836}
837
838void TProcura::Registo(TResultado& resultado, int id, int64_t valor)
839{
840 if (id >= 0 && id < indicador.Count() && indicador[id].indice >= 0)
841 resultado.valor[indicador[id].indice] = valor;
842}
843
845{
846 TString str;
847 TVector<TString> textos = { "📖 Sintaxe comando"," " COR_LEVE "Instâncias:" COR_RESET " A,B,C | A:B | A : B : C" };
848
849 MostraCaixa(textos, 40);
850
851 printf("\n%-2s IDs das instâncias (%d a %d): ", Icon(EIcon::INST), instancia.min, instancia.max);
852
853 str = NovoTexto("");
854 if (!str.Empty())
855 return _TV(str);
856 // colocar apenas a instância atual
857 return TVector<int>() += instancia.valor;
858}
859
861 TVector<int> atual; // parâmetros atuais
862 int id = -1, auxID;
863 TString str;
864
865 ConfiguracaoAtual(atual, LER);
866
867 id = NovaConfiguracao(atual);
868
869 do {
871 "📖 Sintaxe comando",
872 " id / -id " COR_LEVE "- Seleciona configuração como atual ou apaga 'id'" COR_RESET,
873 " Pk = <conj.> " COR_LEVE "- Varia Pk na configuração atual (gera N configs)" COR_RESET,
874 " Pk = <conj.> x Pw = <conj.> " COR_LEVE "- produto externo (gera NxM configs)" COR_RESET,
875 " " COR_LEVE "Sintaxe de <conj.> :" COR_RESET " A,B,C | A:B | A:B:C"
876 };
878
879 MostraCaixa(textos, 70);
880
881 if ((str = NovoTexto("\n✏️ Comando: ")).Empty())
882 break;
883
884 if ((auxID = atoi(str)) != 0) {
885 id = auxID;
887 if (id < 0) {
888 id++;
890 }
891 else if (id > 0) {
892 id--;
893 atual = configuracoes[id];
894 }
895 }
896 else {
898 configuracoes[id] = atual; // alterar atual se necessário
900 }
901 } while (true);
903}
904
908 auto tokens = str.tok();
909
910 // processar todos os itens a iniciar em P, obtendo informação entre quais existe x
911 for (auto& token : tokens) {
912 if (token[0] == 'P') {
913 int param;
914 auto par = token.tok("="); // obter o token do parâmetro e o token dos valores
915 if (par.Count() == 2) {
916 param = atoi(*par.First() + 1);
917 if (param > 0 && param <= parametro.Count()) {
918 valores.Count(valores.Count() + 1);
919 valores.Last() = {};
920 valores.Last() += param; // primeiro valor é ID do parâmetro
921 valores.Last() += _TV(par.Last()); // valores para o parâmetro tomar
922 if (valores.Last().Count() == 2) {
923 // apenas um elemento, altera a configuração atual
924 // (se fosse para alternar, colocava o valor base mais o valor a alternar)
925 int valor = valores.Last().Last();
926 if (valor >= parametro[param - 1].min &&
927 valor <= parametro[param - 1].max)
928 base[param - 1] = valor;
929 }
930 if (valores.Last().Count() <= 2)
931 valores.Count(valores.Count() - 1);
932 }
933 }
934 }
935 else if (token[0] == 'x') {
936 valores.Count(valores.Count() + 1);
937 valores.Last() += 0; // produto externo
938 }
939 }
940
941 // inserir configurações de acordo com o pretendido (produto externo, ou apenas à configuração base)
942 produto = {};
943 for (int i = 0; i < valores.Count(); i++)
944 if (valores[i].First() > 0) { // c.c. é o operador produto externo
945 if (i == valores.Count() - 1 || valores[i + 1].First() != 0) { // não há outro produto externo, colocar na configuração atual
946 produto += i;
948 produto = {};
949 }
950 else
951 produto += i;
952 }
953}
954
956{
957 // inserir os valores do último elemento de forma recursiva
958 int idLista = produto.Pop();
961 int param = lista.First();
962
963 for (int i = 1; i < lista.Count(); i++)
964 if (lista[i] >= parametro[param - 1].min &&
965 lista[i] <= parametro[param - 1].max)
966 {
967 base[param - 1] = lista[i];
968 if (produto.Empty())
970 else
972 }
973 base = backup;
974 produto += idLista;
975}
976
977void TProcura::MostrarConfiguracoes(int detalhe, int atual) {
979 // identificar parametros comuns e distintos entre as parametrizações
980 for (int i = 0; i < configuracoes.First().Count(); i++) {
981 bool igual = true;
982 for (int j = 1; j < configuracoes.Count() && igual; j++)
984 if (igual)
985 comum += i;
986 else
987 distinto += i;
988 }
989 // mostra parametros comuns, evitando repetição em cada configuração
990 MostraParametros(detalhe, &comum, Icon(EIcon::CONF));
991 printf(COR_LEVE " (parâmetros comuns)" COR_RESET);
992
993 if (configuracoes.Count() > 1) {
994 // visualizar configurações atuais, assinalando a atualmente escolhida
995 printf("\n═╪═ Configurações ═══");
996 for (int i = 0; i < configuracoes.Count(); i++) {
997 TString str;
998 str.printf("%-2s [%d]", Icon(EIcon::PARAM), i + 1);
1001 if (i == atual)
1002 printf(" ⭐ atual");
1003 if (atual < 0 && i == 2 && configuracoes.Count() > 10) {
1004 printf("\n │ ...");
1005 i = configuracoes.Count() - 4;
1006 }
1007 }
1008 }
1009 printf("\n═╧═══════════════════");
1010}
1011
1012void TProcura::MostraConjunto(TVector<int> valores, const char* etiqueta) {
1013 printf(" { ");
1014 if (valores.Count() <= 10) {
1015 for (auto ind : valores)
1016 printf("%-2s%d ", etiqueta, ind);
1017 }
1018 else {
1019 for (int i = 0; i <= 2; i++)
1020 printf("%-2s%d ", etiqueta, valores[i]);
1021 printf("… ");
1022 for (int i = valores.Count() - 3; i < valores.Count(); i++)
1023 printf("%-2s%d ", etiqueta, valores[i]);
1024 }
1025 printf("} ");
1026 if (valores.Count() > 10)
1027 printf("#%d", valores.Count());
1028}
1029
1030void TProcura::TesteInicio(TVector<int>& instancias, TVector<int>& configAtual) {
1032 if (configuracoes.Empty()) {
1033 // não foram feitas configurações, utilizar a atual
1036 }
1037 for (auto item : instancias)
1039 item = -1;
1040 instancias -= (-1);
1041 if (mpiID == 0) {
1042 printf("\n\n═╤═ Instâncias ═══");
1043 MostraConjunto(instancias, Icon(EIcon::INST));
1045 }
1046 if (mpiCount < 10 || mpiID == 0)
1047 printf("\n═╤═ %-2s Início do Teste (%-2s%d) ═══",
1048 Icon(EIcon::TESTE), Icon(EIcon::PROCESSO), mpiID);
1049 fflush(stdout);
1050 Cronometro(CONT_TESTE, true); // reiniciar cronómetro global
1051}
1052
1054 if (mpiCount < 10 || mpiID == 0)
1055 printf("\n═╧═ %-2s Fim do Teste (%-2s%d %-2s%s) ═══",
1056 Icon(EIcon::FIM), Icon(EIcon::PROCESSO), mpiID, Icon(EIcon::TEMPO),
1058 fflush(stdout);
1059}
1060
1061
1062// utilizar para executar testes empíricos, utilizando todas as instâncias,
1063// com o último algoritmo executado e configurações existentes
1065 TVector<TResultado> resultados; // guarda as soluções obtidas
1066 TVector<int> atual;
1067 int backupID = instancia.valor;
1068 int nTarefa = 0;
1069 double periodoReporte = 60;
1070
1071 TesteInicio(instancias, atual);
1072
1073 switch (Parametro(NIVEL_DEBUG)) {
1074 case DETALHE: periodoReporte = 10; break;
1075 case COMPLETO: periodoReporte = 0; break; // reporte em todos os eventos
1076 }
1077 Cronometro(CONT_REPORTE, true); // reiniciar cronómetro evento
1078 if (mpiID == 0)
1079 Debug(ATIVIDADE, false,
1080 "\n ├─ %-2sTarefas:%s %-2sInstâncias: %s %-2sConfigurações: %s %-2sProcessos: %s.",
1081 Icon(EIcon::TAREFA), *MostraInt(instancias.Count() * configuracoes.Count()),
1082 Icon(EIcon::INST), *MostraInt(instancias.Count()),
1083 Icon(EIcon::CONF), *MostraInt(configuracoes.Count()),
1084 Icon(EIcon::PROCESSO), *MostraInt(mpiCount)) &&
1085 fflush(stdout);
1086 // percorrer todas as instâncias
1087 for (int configuracao = 0; configuracao < configuracoes.Count(); configuracao++) {
1088 ConfiguracaoAtual(configuracoes[configuracao], GRAVAR);
1089
1090 for (auto inst : instancias) {
1091 // distribuir tarefas por MPI
1092 if ((nTarefa++) % mpiCount != mpiID)
1093 continue;
1094
1096 Debug(ATIVIDADE, false,
1097 "\n ├─ %-2s%-15s %-2s%-5s %-2s%-5s %-2s%-5s %-2s%-5s %-2s",
1098 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE)),
1099 Icon(EIcon::TAREFA), *MostraInt(nTarefa),
1100 Icon(EIcon::INST), *MostraInt(inst),
1101 Icon(EIcon::CONF), *MostraInt(configuracao + 1),
1102 Icon(EIcon::PROCESSO), *MostraInt(mpiID),
1103 Icon(EIcon::IND));
1104 int maxInd = 10;
1105 for (auto ind : resultados.Last().valor) {
1106 if (maxInd-- < 0) {
1107 printf("… ");
1108 break;
1109 }
1110 printf("%s ", *MostraInt(ind));
1111 }
1112 fflush(stdout);
1113 Cronometro(CONT_REPORTE, true);
1114 }
1115 ExecutaTarefa(resultados, inst, configuracao);
1116 }
1117 }
1118
1119 if (ficheiro.Empty())
1121 else {
1122 // gravar resultados em ficheiro CSV
1124
1126 double tempoTotal = tempoLocal;
1127 double tempoMaximo = tempoLocal;
1128#ifdef MPI_ATIVO
1131#endif
1132
1133 if (mpiCount > 1 && modoMPI == 0)
1134 // tenta juntar ficheiros, caso existam os ficheiros dos outros processos
1136 if (mpiID == 0)
1137 Debug(ATIVIDADE, false,
1138 "\n ├─ %-2s Ficheiro %s.csv gravado.\n"
1139 " │ %-2s Tempo real: %s",
1140 Icon(EIcon::RESULT), *ficheiro,
1141 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE))) &&
1142 Debug(ATIVIDADE, false, "\n │ %-2s CPU total: %s",
1143 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE) * mpiCount)) &&
1144 Debug(ATIVIDADE, false, "\n │ %-2s Utilização: %.1f%%",
1145 Icon(EIcon::TAXA), 100. * tempoTotal / (tempoMaximo * mpiCount));
1146 }
1147
1148 ConfiguracaoAtual(atual, GRAVAR);
1150 Inicializar();
1151 TesteFim();
1152}
1153
1155{
1156#ifdef MPI_ATIVO
1157 int dados[3] = { 0, 0, 0 }; // instância, configuração
1158 double esperaTrabalhadores = 0, esperaGestor = 0;
1159 TVector<double> terminou; // instante em que terminou cada trabalhador
1161 TVector<int> atual;
1162 double periodoReporte = 60;
1163
1164 TesteInicio(instancias, atual);
1165
1166 switch (Parametro(NIVEL_DEBUG)) {
1167 case DETALHE: periodoReporte = 10; break;
1168 case COMPLETO: periodoReporte = 0; break;
1169 }
1170 for (int i = 1; i < mpiCount; i++)
1171 trabalhador += i;
1172
1173 terminou.Count(mpiCount);
1174 terminou.Reset(0);
1175
1176 // Ciclo:
1177 // 1. Enviar trabalho para os escravos
1178 // 2. Encerrar escravos a mais
1179 // 3. Receber resultados e repetir 1 ou 2 conforme as necessidades
1180
1181 TVector<TResultado> resultados; // guarda as soluções obtidas
1182 TVector<TResultado> tarefas; // guarda informação apenas das tarefas a realizar (sem resultados)
1183 Cronometro(CONT_REPORTE, true); // reiniciar cronómetro evento
1184
1185 // construir todas as tarefas
1186 for (int configuracao = 0; configuracao < configuracoes.Count(); configuracao++)
1187 for (auto inst : instancias)
1188 tarefas += { inst, configuracao };
1189
1190 int totalTarefas = tarefas.Count();
1191 Debug(ATIVIDADE, false, "\n ├─ %-2sTarefas:%s %-2sInstâncias: %s %-2sConfigurações: %s %-2sProcessos: %s.",
1192 Icon(EIcon::TAREFA), *MostraInt(tarefas.Count()),
1193 Icon(EIcon::INST), *MostraInt(instancias.Count()),
1194 Icon(EIcon::CONF), *MostraInt(configuracoes.Count()),
1195 Icon(EIcon::PROCESSO), *MostraInt(trabalhador.Count() + 1)) &&
1196 fflush(stdout);
1197
1198 // dar uma tarefa a cada escravo
1199 while (!tarefas.Empty() && !trabalhador.Empty()) {
1200 auto tarefa = tarefas.Pop();
1201 dados[0] = tarefa.instancia;
1202 dados[1] = tarefa.configuracao;
1203 trabalhar += trabalhador.Last();
1205 esperaTrabalhadores += Cronometro(CONT_TESTE); // estava parado até esta altura
1206 }
1207 // caso existam escravos sem trabalho, mandar fechar todos, não há mais tarefas
1208 dados[0] = dados[1] = -1;
1209 while (!trabalhador.Empty()) {
1210 auto trabalhadorID = trabalhador.Pop();
1213 }
1214
1215 // receber resultados e continuar a dar trabalho caso exista
1216 while (!trabalhar.Empty()) {
1218
1222 resultados += {dados[0], dados[1]};
1223 resultados.Last().valor.Count(dados[2]);
1224 trabalhar -= stat.MPI_SOURCE;
1225 trabalhador += stat.MPI_SOURCE;
1226 MPI_Recv(resultados.Last().valor.Data(), dados[2], MPI_LONG_LONG,
1228 // tempo de espera do trabalhador
1229 esperaTrabalhadores += (double)((int64_t)resultados.Last().valor.Pop()) / 1000.;
1230
1232 // mostrar uma linha por cada execução
1233 Debug(ATIVIDADE, false,
1234 "\n ├─ %-2s%-15s %-2s%-5s %-2s%-5s %-2s%-5s %-2s%-5s %-2s ",
1235 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE)),
1236 Icon(EIcon::TAREFA), *MostraInt(totalTarefas - tarefas.Count()),
1237 Icon(EIcon::INST), *MostraInt(resultados.Last().instancia),
1238 Icon(EIcon::CONF), *MostraInt(resultados.Last().configuracao),
1239 Icon(EIcon::PROCESSO), *MostraInt(trabalhador.Last()),
1240 Icon(EIcon::IND));
1241 int maxInd = 10;
1242 for (auto ind : resultados.Last().valor) {
1243 if (maxInd-- < 0) {
1244 printf("… ");
1245 break;
1246 }
1247 printf("%s ", *MostraInt(ind));
1248 }
1249 fflush(stdout);
1250 Cronometro(CONT_REPORTE, true);
1251 }
1252
1253 // ainda há tarefas
1254 if (!tarefas.Empty()) {
1255 auto tarefa = tarefas.Pop();
1256 dados[0] = tarefa.instancia;
1257 dados[1] = tarefa.configuracao;
1258 trabalhar += trabalhador.Last();
1260 }
1261 else { // tudo feito, mandar sair
1262 dados[0] = dados[1] = -1;
1263 auto trabalhadorID = trabalhador.Pop();
1266 }
1267 }
1268
1269 // contar a espera dos trabalhadores, após terminarem
1270 for (int i = 1; i < mpiCount; i++)
1272
1273 // escrever o ficheiro de resultados
1274 int backupCount = mpiCount;
1278 mpiCount = 1; // forçar a escrita do ficheiro apenas neste processo
1280 Debug(ATIVIDADE, false,
1281 "\n ├─ %-2s Ficheiro %s.csv gravado.\n"
1282 " │ %-2s Tempo real: %s",
1283 Icon(EIcon::RESULT), *ficheiro,
1284 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE))) &&
1285 Debug(ATIVIDADE, false, "\n │ %-2s CPU total: %s",
1286 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE) * (backupCount - 1))) &&
1287 Debug(ATIVIDADE, false, "\n │ %-2s Espera do gestor: %s",
1288 Icon(EIcon::TEMPO), *MostraTempo(esperaGestor)) &&
1289 Debug(ATIVIDADE, false, "\n │ %-2s Espera trabalhadores: %s",
1290 Icon(EIcon::TEMPO), *MostraTempo(esperaTrabalhadores)) &&
1291 Debug(ATIVIDADE, false, "\n │ %-2s Utilização:\n │ - Total: %.1f%%\n │ - Gestor: %.1f%%\n │ - Trabalhadores: %.1f%% ",
1292 Icon(EIcon::TAXA), taxaUtilizacao * 100, taxaUtilizacaoG * 100, taxaUtilizacaoT * 100);
1294
1295 TesteFim();
1296#endif
1297}
1298
1300{
1301#ifdef MPI_ATIVO
1302 int dados[3] = { 0, 0, 0 }; // instância, configuração
1303 // Ciclo:
1304 // 1. Solicitar tarefa ao mestre
1305 // 2. Executar tarefa
1306 // 3. Enviar resultados ao mestre
1307 // 4. Repetir até receber ordem de paragem
1308
1309 TVector<TResultado> resultados; // guarda as soluções obtidas
1310 TVector<int> atual;
1311
1312 TesteInicio(instancias, atual);
1313
1314 for (;;) {
1315 // receber nova tarefa
1317 if (dados[0] < 0)
1318 break;
1319
1321
1322 // enviar registo para master, e apagar
1323 // dados[0] e dados[1] já têm a configuração e instância
1324 dados[2] = resultados.Last().valor.Count() + 1;
1327 // colocar a espera no final do vetor de resultados
1328 resultados.Last().valor += (int64_t)((Cronometro(CONT_TESTE) - inicioEspera) * 1000 + 0.5);
1329 MPI_Send(resultados.Last().valor.Data(), dados[2], MPI_LONG_LONG, 0, TAG_VALORES, MPI_COMM_WORLD);
1330
1331 resultados.Pop();
1332 }
1333
1334 // saída, enviar o tempo de trabalho e tempo de espera totais
1335
1336 TesteFim();
1337#endif
1338}
1339
1340void TProcura::ExecutaTarefa(TVector<TResultado>& resultados, int inst, int conf)
1341{
1342 // carregar a configuração
1345 Parametro(NIVEL_DEBUG) = NADA; // remover informação de debug do algoritmo, já que é um teste empírico
1346 instancia.valor = inst;
1347 // carregar instância
1348 Inicializar();
1349 // executar um algoritmo
1351
1352 if (!ficheiroGravar.Empty()) {
1353 Gravar();
1355 }
1356 else
1360
1362
1363 if (resultado >= 0) {
1364 mpiID == 0 && Debug(COMPLETO, false, "%-2s%-5d", Icon(EIcon::SUCESSO), resultado);
1365 }
1366 else {
1367 if (Parar())
1368 mpiID == 0 && Debug(COMPLETO, false, "%-2s", Icon(EIcon::INSUC));
1369 if (TempoExcedido())
1370 mpiID == 0 && Debug(COMPLETO, false, "%-2s", Icon(EIcon::TEMPO));
1371 if (memoriaEsgotada)
1372 mpiID == 0 && Debug(COMPLETO, false, "%-2s", Icon(EIcon::MEMORIA));
1373 if (resultado < 0 && !Parar()) { //Instância Impossível! (se algoritmo completo) ");
1374 mpiID == 0 && Debug(COMPLETO, false, "%-2s%-2s", Icon(EIcon::SUCESSO), Icon(EIcon::IMP));
1375 resultados.Last().solucao = "vazio";
1376 }
1377 else // não resolvido, cancelar resultados
1378 resultados.Last().valor.First() = RES_NAO_RESOLVIDO;
1379 }
1380 if (mpiID == 0 && Parametro(NIVEL_DEBUG) >= COMPLETO) {
1381 printf("%-2s ", Icon(EIcon::IND));
1382 for (auto ind : resultados.Last().valor)
1383 printf("%" PRId64 " ", ind);
1384 }
1385}
1386
1387// processa os argumentos da função main
1388void TProcura::main(int argc, char* argv[], TString nome) {
1392 bool configIntroduzido = false; // caso sejam dadas configurações, remover as existentes
1393 TVector<int> referencias = { 0,100,0,100000 }; // custoMin, custoMax, tempoMin, tempoMax
1394
1396
1397 if (argc <= 1 || (argc == 2 && strcmp(argv[1], "-K") == 0)) {
1398 if (argc == 2) // permitir ligar modo KMGT, no modo interativo
1399 modoKMGT = 1;
1400 TesteManual(*nome);
1401 return;
1402 }
1403 else if (strcmp(argv[1], "-h") == 0) {
1405 return;
1406 }
1407
1408 // 1:10 --- conjunto de instâncias (idêntico ao interativo)
1409 instancias = argv[1];
1410 if (instancias.Empty()) {
1412 return;
1413 }
1414
1415 fichResultados = "resultados";
1416
1418
1419 // opcionais:
1420 // -R resultados --- ficheiro de resultados em CSV (adicionada extensão .csv)
1421 // -S solucoes [custoMin custoMax tempoMin tempoMax [<ids>]] --- ficheiro de solucoes em CSV
1422 // -F instancia_ --- prefixo dos ficheiros de instâncias
1423 // -I 2,1,3 --- indicadores selecionados por ordem
1424 // -P P1=1:3 x P2=0:2 --- formatação de parâmetros (idêntico ao interativo)
1425 for (int i = 2; i < argc; i++) {
1426 if (strcmp(argv[i], "-R") == 0 && i + 1 < argc) {
1427 (fichResultados = "").printf("%s", argv[++i]);
1428 }
1429 else if (strcmp(argv[i], "-S") == 0 && i + 1 < argc) {
1430 (fichSolucoes = "").printf("%s", argv[++i]);
1431 // carregar as 4 referências, caso existam
1432 if (i + 1 < argc && isdigit(argv[i + 1][0])) {
1433 referencias = {};
1434 for (auto& token : TString(argv[++i]).tok(","))
1436 if (referencias.Count() != 4) // usar valores por omissão se não forem dadas as 4 referências
1437 referencias = { 0,100,0,100000 };
1438 }
1439 // e os ids de instâncias impossíveis, caso existam
1440 if (i + 1 < argc && isdigit(argv[i + 1][0]))
1441 impossiveis = argv[++i];
1442 }
1443 else if (strcmp(argv[i], "-F") == 0 && i + 1 < argc) {
1444 (ficheiroInstancia = "").printf("%s", argv[++i]);
1445 }
1446 else if (strcmp(argv[i], "-FG") == 0 && i + 1 < argc) {
1447 (ficheiroGravar = "").printf("%s", argv[++i]);
1448 }
1449 else if (strcmp(argv[i], "-M") == 0 && i + 1 < argc) {
1450 if ((modoMPI = atoi(argv[++i])) != 1)
1451 modoMPI = 0; // apenas 0 ou 1
1452 }
1453 else if (strcmp(argv[i], "-K") == 0 && i + 1 < argc) {
1454 if ((modoKMGT = atoi(argv[++i])) != 1)
1455 modoKMGT = 0; // apenas 0 ou 1
1456 }
1457 else if (strcmp(argv[i], "-G") == 0 && i + 1 < argc) {
1458 if ((gravarSolucao = atoi(argv[++i])) != 1)
1459 gravarSolucao = 0; // apenas 0 ou 1
1460 }
1461 else if (strcmp(argv[i], "-I") == 0 && i + 1 < argc) {
1462 indAtivo = {};
1463 for (auto& token : TString(argv[i + 1]).tok(",")) {
1464 indAtivo += (atoi(token) - 1);
1465 indicador[indAtivo.Last()].indice = indAtivo.Count() - 1;
1466 }
1467 }
1468 else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) {
1470 if (!configIntroduzido) { // limpa configurações anteriores ou por omissão
1471 configIntroduzido = true;
1472 configuracoes = {};
1473 }
1474 // o resto é para concatenar e enviar, até outro "-P" ou fim
1475 argParametros = "";
1476 while (++i < argc && strcmp(argv[i], "-P") != 0)
1477 argParametros.printf(" %s", argv[i]);
1482
1483 if (i >= argc) // era o último conjunto de argumentos
1484 break;
1485 else
1486 i -= 2; // recuar para processar o -P seguinte
1487 }
1488 }
1489
1490 if (!fichSolucoes.Empty()) {
1491 // dado ficheiro de soluções, apenas validar as soluções, não executar o teste empírico
1493 }
1494 else {
1495 // arrancar MPI apenas após processar os argumentos
1497
1498 if (modoMPI == 0 || mpiCount == 1)
1499 // divisão estática ou execução em série
1501 else {
1502 if (mpiID == 0)
1503 // processo mestre
1505 else
1506 // processos escravos
1508 }
1509
1510 FinalizaMPI();
1511 }
1512}
1513
1515 printf(
1516 "Uso: %s <instâncias> [opções]\n"
1517 " <instâncias> Conjunto de IDs: A | A,B,C | A:B[:C]\n"
1518 "Opções:\n"
1519 " -R <ficheiro> Nome do CSV de resultados (omissão: resultados.csv)\n"
1520 " -S solucoes [custoMin,custoMax,tempoMin,tempoMax [<ids>]]\n"
1521 " caso exista ficheiro de soluções, pretende-se apenas validação\n"
1522 " pode-se dar referências de custo min/max e tempo min/max para indicador de desempenho\n"
1523 " <ids> - identificação das instâncias impossíveis\n"
1524 " -F <prefixo> Prefixo para leitura da instância por ficheiro (omissão: vazio)\n"
1525 " -FG <prefixo> Prefixo para gravação da instância em ficheiro (omissão: vazio)\n"
1526 " -M <modo> Modo MPI: 0 = divisão estática, 1 = gestor-trabalhador\n"
1527 " -G <0/1> Gravar solução (sequência de ações): 0 = não grava, 1 = grava\n"
1528 " -I <ind> Lista de indicadores (e.g. 2,1,3)\n"
1529 " -K <modo> 1 - ativa formatação humana de números (K/M/G/T), 0 - desativa\n"
1530 " -h Esta ajuda\n"
1531 " -P <expr> Parâmetros (e.g. P1=1:3 x P2=0:2) - valores para cada parâmetro, distintos dos por omissão\n"
1532 "Exemplo: %s 1:5 -R out -F fich_ -I 3,1,4,2 -P P1=1:5 x P6=1,2 \n"
1533 " Executar sem argumentos entra em modo interativo, para explorar todos os parâmetros e indicadores\n",
1535 );
1539}
1540
1541
1542bool TProcura::RelatorioCSV(TVector<TResultado>& resultados, TString ficheiro, bool parametros) {
1543 TString nome;
1545 if (mpiCount > 1)
1546 nome.printf("%s_%d.csv", *ficheiro.tok().First(), mpiID);
1547 else
1548 nome.printf("%s.csv", *ficheiro.tok().First());
1549
1550 // cabeçalho: instância, parametros, indicadores
1551 linhas += TString("Instância;");
1552 if (parametros) {
1553 for (int i = 0; i < parametro.Count(); i++)
1554 linhas.Last().printf("P%d(%s);", i + 1, *parametro[i].nome);
1555 for (auto item : indAtivo)
1556 linhas.Last().printf("I%d(%s);", item + 1, *indicador[item].nome);
1557 linhas.Last().printf("Solução");
1558 }
1559 else {
1560 // apenas soluções: válidas, inválidas, melhor, pior
1561 linhas.Last().printf("Válidas;Inválidas;Melhor;Pior;Tempo(ms)");
1562 }
1563 for (auto& res : resultados) {
1564 linhas += TString().printf("%d;", res.instancia);
1565 if (parametros) {
1566 for (int j = 0; j < parametro.Count(); j++)
1567 // ver se parametro j está ativo na configuração configuracoes[res.configuracao]
1568 if (!ParametroAtivo(j, &(configuracoes[res.configuracao])))
1569 linhas.Last().printf(";"); // parametro inativo, não mostrar
1570 else if (parametro[j].nomeValores.Empty())
1571 linhas.Last().printf("%d;", configuracoes[res.configuracao][j]); // mostrar valor
1572 else
1573 linhas.Last().printf("%d:%s;", // mostrar valor e texto
1574 configuracoes[res.configuracao][j],
1575 *parametro[j].nomeValores[configuracoes[res.configuracao][j] - parametro[j].min]);
1576 for (auto ind : indAtivo)
1577 linhas.Last().printf("%" PRId64 ";", Registo(res, ind));
1578
1579 if (gravarSolucao) {
1580 if (!res.solucao.Empty()) {
1581 for (auto& acao : res.solucao)
1582 linhas.Last().printf("%s ", *acao);
1583 }
1584 else {
1585 // imprimir todos os valores após os indicadores
1586 for (int i = indicador.Count(); i < res.valor.Count(); i++)
1587 linhas.Last().printf("%" PRId64 ";", res.valor[i]);
1588 }
1589 }
1590 }
1591 else {
1592 // apenas soluções: válidas, inválidas, melhor, pior, tempo
1593 for (auto item : res.valor)
1594 linhas.Last().printf("%" PRId64 ";", item);
1595 }
1596 }
1597
1598 nome.writeLines(linhas);
1599
1600 return true;
1601}
1602
1604{
1605 if (ultimo) {
1606 if (!resultados.Empty() && !indAtivo.Empty()) {
1607 int col = 2;
1608 MostraCaixa("Indicadores", ECaixaParte::Topo, 70, true, 0, Icon(EIcon::IND));
1610 for (auto ind : indAtivo) {
1611 if (col > 2)
1612 col += printf(" | ");
1613 if (col >= 70) {
1615 col = 2;
1616 }
1617 col += printf(COR_LEVE "I%d(%s):" COR_RESET " %s", ind + 1,
1618 *indicador[ind].nome, *MostraInt(Registo(resultados.Last(), ind))) - COR_LEVE_TAM;
1619 }
1621 }
1622 return;
1623 }
1624
1625 TVector<TResultado> total; // totais por cada configuração
1627 for (int i = 0; i < total.Count(); i++) {
1628 total[i].instancia = total[i].configuracao = 0;
1629 total[i].valor.Count(indicador.Count());
1630 total[i].valor.Reset(0);
1631 }
1632 TVector<int> larguras = { 6,7,11,11 };
1633 TVector<TString> titulosVazios = { "", "", "", "" };
1635 Icon(EIcon::INST),
1636 Icon(EIcon::CONF),
1637 Icon(EIcon::VALOR),
1638 Icon(EIcon::TEMPO)
1639 };
1640
1641 // mostrar os resultados apenas do custo e tempo
1645
1646
1647 for (auto& res : resultados) {
1649 if (Registo(res, IND_RESULTADO) >= -1)
1650 total[res.configuracao].instancia++;
1651
1652 str[0].printf("%d", res.instancia);
1653 str[1].printf("%d", res.configuracao + 1);
1654 str[2].printf("%" PRId64, Registo(res, IND_RESULTADO));
1655 str[3].printf("%" PRId64, Registo(res, IND_TEMPO));
1656
1658
1659 // somar tudo
1660 for (auto ind : indAtivo)
1661 Registo(total[res.configuracao], ind,
1662 Registo(total[res.configuracao], ind) +
1663 Registo(res, ind));
1664 }
1666
1667 // tabela com os totais por configuração
1668 for (int i = 0; i < total.Count(); i++) {
1669 TString str;
1670 int col = 2;
1671 str.printf("%-2s Total %-2s%d", Icon(EIcon::TAXA), Icon(EIcon::CONF), i + 1);
1674 for (auto ind : indAtivo) {
1675 col += printf(COR_LEVE "%s:" COR_RESET " ", *indicador[ind].nome) - COR_LEVE_TAM;
1676 col += printf("%" PRId64 " ", Registo(total[i], ind));
1677 if (col > 70) {
1679 col = 2;
1680 }
1681 }
1682 if (col > 70)
1684 printf(COR_LEVE "Instâncias resolvidas:" COR_RESET " %d", total[i].instancia);
1686 }
1687 // mostrar torneio entre configurações
1689 printf("\n");
1690}
1691
1693 TVector<TVector<int>> torneio; // pares de configurações: 1 melhor, 0 igual -1 pior
1695 for (int i = 0; i < torneio.Count(); i++) {
1696 torneio[i].Count(configuracoes.Count());
1697 torneio[i].Reset(0);
1698 }
1699 // registar resultados mediante o melhor resultado
1700 for (int i = 0; i < configuracoes.Count(); i++) {
1702 for (int j = 0; j < configuracoes.Count(); j++)
1703 if (i != j) {
1705 // resultados sempre por mesma ordem de instância
1706 for (int k = 0; k < configuracaoI.Count() && k < configuracaoJ.Count(); k++)
1708 }
1709 }
1711}
1712
1714{
1715 MostraCaixa("Indicadores", ECaixaParte::Topo, 70, true, 0, Icon(EIcon::IND));
1716 for (int i = 0; i < indicador.Count(); i++) {
1718 printf(COR_LEVE "I%d(%s):" COR_RESET " ", i + 1, *indicador[i].nome);
1719 if (indicador[i].indice < 0)
1720 printf("%-2sinativo ", Icon(EIcon::NSEL));
1721 else
1722 printf("%-2s%dº lugar ", Icon(EIcon::SEL), indicador[i].indice + 1);
1724 printf(COR_LEVE "%s" COR_RESET, *indicador[i].descricao);
1725 }
1727}
1728
1729
1731{
1733 pontos.Count(torneio.Count());
1734 pontos.Reset(0);
1735 // registar resultados mediante o melhor resultado
1736 for (int i = 0; i < torneio.Count(); i++)
1737 for (int j = 0; j < torneio.Count(); j++)
1738 if (i != j) {
1739 pontos[i] += torneio[i][j];
1740 if (jogo) // contar pontos perdidos de pretas
1741 pontos[j] -= torneio[i][j];
1742 }
1743
1744 // mostrar tabela do torneio
1745 printf("\n%-2s Torneio (#instâncias melhores):", Icon(EIcon::TORNEIO));
1746 BarraTorneio(true);
1747 for (int i = 0; i < pontos.Count(); i++) {
1748 printf("\n%2d", i + 1);
1749 for (int j = 0; j < pontos.Count(); j++)
1750 if (i == j)
1751 printf(" |");
1752 else
1753 printf("%3d |", torneio[i][j]);
1754 // no final colocar os pontos totais
1755 printf("%3d", pontos[i]);
1756 BarraTorneio(false);
1757 }
1758}
1759
1762 for (auto& res : resultados)
1763 if (res.configuracao == configuracao)
1764 extracao += res;
1765 return extracao;
1766}
1767
1768
1769void TProcura::BarraTorneio(bool nomes) {
1770 // barra inical/final: |----|----|----|
1771 printf("\n |");
1772 for (int i = 0; i < configuracoes.Count(); i++)
1773 if (nomes)
1774 printf("-%02d-|", i + 1);
1775 else
1776 printf("----|");
1777}
1778
1779
1781 // se não resolvido por ambos, retornar igualdade (assumir código -1 para impossível, -2 para não resolvido, menor é melhor)
1783 return 0;
1784 // se igual no custo e o tempo menor que 100, retornar igualdade
1787 return 0;
1788 // primeiro custo (ou não resolvido, -2)
1789 if ((Registo(base, IND_RESULTADO) == -2 &&
1793 return -1;
1794 if ((Registo(base, IND_RESULTADO) > -2 &&
1796 (Registo(base, IND_RESULTADO) > 0 &&
1798 return 1;
1799 // agora o tempo
1800 return Registo(base, IND_TEMPO) < Registo(alternativa, IND_TEMPO) ? 1 : -1;
1801}
1802
1804{
1805 if (TempoExcedido())
1806 Mensagem(Icon(EIcon::INSUC), " Tempo excedido");
1807 else if (memoriaEsgotada)
1808 Mensagem(Icon(EIcon::INSUC), " Memória esgotada");
1809}
1810
1811// MostrarSolucao: definir para visualizar a solução
1814 printf("\nSolução: ");
1815 for (auto& x : solucao)
1816 printf("%" PRId64 " ", x);
1817 printf(".");
1818}
1819
1820
1822 TString str;
1824 printf("%s", *prompt);
1825 if (fgets(str.Data(), BUFFER_SIZE, stdin))
1826 if (strlen(str) > 1)
1827 return atoi(str);
1828 return NAO_LIDO;
1829}
1830
1831// ler uma string
1833#ifdef _WIN32
1834 TString str;
1836 printf("%s", *prompt);
1837
1838 if (fgets(str.Data(), BUFFER_SIZE, stdin) == nullptr)
1839 return TString("");
1840
1841 // remover o '\n' do final
1842 int len = (int)strlen(str);
1843 if (len > 0 && str[len - 1] == '\n')
1844 str[len - 1] = 0;
1845
1846 // retorna nova TString, com o tamanho certo
1847 return TString(*str);
1848
1849#else
1850 // código para funcionar no Linux, no Windoss já isto é feito de omissõa
1851 // permite editar e recuperar textos
1854 int histIndex = historico.Count();
1855 compat::ModoNativo modo; // ativa raw mode até sair do escopo
1856 printf("%s", *prompt);
1857
1858 while (true) {
1859 int c = getchar();
1860
1861 if (c == '\n' || c == '\r') { // Enter
1862 printf("\n");
1863 if (!buffer.Empty())
1864 historico += buffer;
1865 return TString(*buffer);
1866 }
1867
1868 if (c == 127 || c == 8) { // Backspace
1869 if (!buffer.Empty()) {
1870 buffer.Pop();
1871 buffer.Last() = 0;
1872 printf("\b \b");
1873 }
1874 continue;
1875 }
1876
1877 if (c == 27) { // ESC sequence
1878 if (getchar() == '[') {
1879 switch (getchar()) {
1880 case 'A': // ↑
1881 if (histIndex > 0) {
1882 --histIndex;
1883 while (!buffer.Empty()) {
1884 buffer.Pop();
1885 buffer.Last() = 0;
1886 printf("\b \b");
1887 }
1888 printf("%s", *(buffer = historico[histIndex]));
1889 }
1890 break;
1891 case 'B': // ↓
1892 if (histIndex < historico.Count() - 1) {
1893 ++histIndex;
1894 while (!buffer.Empty()) {
1895 buffer.Pop();
1896 buffer.Last() = 0;
1897 printf("\b \b");
1898 }
1899 printf("%s", *(buffer = historico[histIndex]));
1900 }
1901 break;
1902 }
1903 }
1904 continue;
1905 }
1906
1907 // caracteres normais
1908 buffer.printf("%c", c);
1909 putchar(c);
1910 }
1911 return TString(*buffer);
1912#endif
1913}
1914
1916 if (instancia.max != instancia.min) {
1917 int resultado;
1918 TString texto;
1919
1920 MostraCaixa("Instância", ECaixaParte::Topo, 70, true, 0, Icon(EIcon::INST));
1922 printf(COR_LEVE "ID atual:" COR_RESET " %d " COR_LEVE "Intervalo:" COR_RESET " [%d–%d] ",
1925 printf(COR_LEVE "Prefixo atual:" COR_RESET " '%s' ", *ficheiroInstancia);
1927 texto = NovoTexto("\nNovo ID (ENTER mantém) ou novo prefixo (texto): ");
1928 resultado = atoi(texto);
1929 if (resultado != 0 || texto.Empty()) {
1930 if (resultado != 0)
1933 }
1934 else if (texto.Count() < 256)
1936 }
1937 else
1939 Inicializar();
1940}
1941
1942
1943int TProcura::Dominio(int& variavel, int min, int max) {
1944 if (variavel < min)
1945 variavel = min;
1946 if (variavel > max)
1947 variavel = max;
1948 return variavel;
1949}
1950
1951void TProcura::InicializaMPI(int argc, char* argv[])
1952{
1953#ifdef MPI_ATIVO
1954 MPI_Init(&argc, &argv);
1957#endif
1958}
1959
1961{
1962#ifdef MPI_ATIVO
1963 MPI_Finalize();
1964#endif
1965}
1966
1968 TString prefixo, int modoCor, bool duplaColuna)
1969{
1970 if (Parametro(NIVEL_DEBUG) < nivel)
1971 return;
1972 printf("\n%s%-4s ", *prefixo, *tipo);
1973 for (int i = 0; i < 10 && i < tabela.Count(); i++)
1974 printf("%4d ", i + 1);
1975 printf("\n%s────┼", *prefixo);
1976 for (int i = 0; i < 10 && i < tabela.Count(); i++)
1977 printf("────┼");
1978 for (int i = 0; i < tabela.Count(); i++) {
1979 if (i % 10 == 0)
1980 printf("\n%s%4d│", *prefixo, i);
1981 if (modoCor > 0 && modoCor <= 1000000) // conteúdo com cor de 1 a modoCor
1982 DebugHSL(tabela[i] * 360.0f / modoCor);
1983 else if (modoCor > 1000000) // 0 - verde e maiorCusto vermelho
1984 DebugHSL((1 - 1.0f * tabela[i] / (modoCor - 1000000)) * 120, .75, .5, false);
1985 else if (modoCor < 0) // índice com a cor
1986 DebugHSL((i + 1) * 360.0f / tabela.Count());
1987 printf("%4d", tabela[i]);
1988 DebugHSL();
1989 if (duplaColuna && i % 2 == 0)
1990 printf("⇄");
1991 else
1992 printf("│");
1993 }
1994}
1995
1997{
1998 // ficheiros CSV com o mesmo cabeçalho, ficheiro0.csv, ficheiro1.csv, ..., ficheiroN.csv
2000
2001 // verifica se existem os ficheiros intermédios
2002 for (int i = 0; i < mpiCount; i++)
2003 if (TString().printf("%s_%d.csv", *ficheiro, i).readLines().Empty())
2004 // não existe este ficheiro, ainda não está tudo
2005 return false;
2006
2007 for (int i = 0; i < mpiCount; i++) {
2008 linhas = TString().printf("%s_%d.csv", *ficheiro, i).readLines();
2009 if (i == 0)
2011 else
2012 // não incluir a linha de cabeçalho
2013 for (int j = 1; j < linhas.Count(); j++)
2014 todasLinhas += linhas[j];
2015
2016 remove(TString().printf("%s_%d.csv", *ficheiro, i)); // apagar ficheiro intermédio
2017 }
2019 return true;
2020}
2021
2023{
2025
2026 // adicionar os indicadores e parâmetros específicos
2027 indicador += ind;
2028 parametro += par;
2029
2030 // atualizar os valores por omissão
2031 omissao.Count(parametro.Count());
2032 for (int i = 0; i < parametro.Count(); i++)
2033 omissao[i] = parametro[i].valor;
2034
2035 instancia = inst;
2036
2037 indValores.Count(indicador.Count()).Reset(0);
2038
2039 // colocar todos os indicadores ativos por ordem de ID
2040 indAtivo = {};
2041 for (int i = 0; i < indicador.Count(); i++)
2042 indAtivo += i;
2043
2044 // corrigir prefixos ineixistentes
2045 while (parPrefixo.Count() < parametro.Count())
2046 parPrefixo += TString("");
2047 while (indPrefixo.Count() < indicador.Count())
2048 indPrefixo += TString("");
2049}
2050
2052{
2054 int error;
2055
2057
2058 // construir as opções que são distintas dos valores por omissão
2059 for (int i = 0; i < parametro.Count(); i++)
2060 if (Parametro(i) != omissao[i])
2061 opcoes.printf("%s%d ", *parPrefixo[i], Parametro(i));
2062
2063 error = system(TString().printf("%s %s %s%d > %s",
2065 if (error == -1) {
2066 Mensagem(Icon(EIcon::INSUC), " Erro ao executar o comando.");
2067 return RES_NAO_RESOLVIDO;
2068 }
2069 else if (error != 0) {
2070 Mensagem(Icon(EIcon::INSUC), " O comando retornou um código de erro: %d", error);
2071 return RES_NAO_RESOLVIDO;
2072 }
2073
2074 // extrair indicadores com base no indPrefixo
2075 for (auto& linha : resultFile.readLines())
2076 for (int i = 0; i < indPrefixo.Count(); i++)
2077 if (!indPrefixo[i].Empty()) {
2078 const char* ptr = strstr(linha, indPrefixo[i]);
2079 if (ptr)
2080 indValores[i] = (int)(atof(ptr + indPrefixo[i].Count() - 1) + 0.5);
2081 }
2082
2084 remove(resultFile); // apagar ficheiro de resultados intermédio
2085 return 0;
2086}
2087
2089{
2090 if (id < indValores.Count())
2091 return indValores[id];
2092 return TProcura::Indicador(id);
2093}
constexpr int BUFFER_SIZE
constexpr int BUFFER_SIZE
Definition TProcura.cpp:8
@ TAG_TRABALHO
Definition TProcura.h:125
@ TAG_VALORES
Definition TProcura.h:127
@ TAG_CABECALHO
Definition TProcura.h:126
#define COR_LEVE
Definition TProcura.h:34
@ GRAVAR
Definition TProcura.h:104
@ LER
Definition TProcura.h:104
constexpr int RES_INVALIDO
Definition TProcura.h:19
@ IND_TEMPO
tempo em milissegundos consumidos
Definition TProcura.h:46
@ IND_ITERACOES
número de iterações consumidas
Definition TProcura.h:47
@ IND_RESULTADO
resultado do algoritmo (>=0 custo da solução, -1 impossível, -2 não resolvido)
Definition TProcura.h:45
constexpr int COR_LEVE_TAM
Definition TProcura.h:42
constexpr int NAO_LIDO
Definition TProcura.h:14
#define COR_RESET
Definition TProcura.h:35
#define COR_INATIVO_LEVE
Definition TProcura.h:39
constexpr int RES_NAO_RESOLVIDO
Definition TProcura.h:18
constexpr int RES_VAZIO
Definition TProcura.h:20
ENivelDebug
Níveis de detalhamento para debug.
Definition TProcura.h:90
@ ATIVIDADE
Apenas eventos principais.
Definition TProcura.h:92
@ COMPLETO
Mostra toda a execução detalhadamente.
Definition TProcura.h:95
@ NADA
Sem informações de debug.
Definition TProcura.h:91
@ DETALHE
Debug detalhada sobre estados e decisões.
Definition TProcura.h:94
constexpr int RES_IMPOSSIVEL
Definition TProcura.h:17
@ CONT_TESTE
Tempo total do teste (todas as execuções)
Definition TProcura.h:113
@ CONT_REPORTE
Tempo entre mensagens durante o teste.
Definition TProcura.h:114
@ CONT_ALGORITMO
Tempo da execução do algoritmo por instância.
Definition TProcura.h:112
@ NIVEL_DEBUG
Nível de debug, de reduzido a completo.
Definition TProcura.h:71
@ LIMITE_TEMPO
Tempo limite em segundos.
Definition TProcura.h:73
#define COR_ATIVO_LEVE
Definition TProcura.h:38
ECaixaParte
Define as partes de uma caixa de texto para exibição formatada.
Definition TProcura.h:136
#define COR_INATIVO
Definition TProcura.h:37
#define COR_ATIVO
Definition TProcura.h:36
TVector< int > _TV(const char *str)
Definition TVector.h:1510
TVector< int64_t > indValores
Definition TProcura.h:895
void ResetParametros()
Reset parâmetros, assumindo variáveis da classe definidas.
int ExecutaAlgoritmo()
Executa o algoritmo e extrai os indicadores.
int64_t Indicador(int id)
retorna indicadores após execução
TVector< TString > indPrefixo
Definition TProcura.h:885
TVector< int > omissao
Definition TProcura.h:896
TVector< TIndicador > ind
Definition TProcura.h:884
TVector< TString > parPrefixo
Definition TProcura.h:889
TVector< TParametro > par
Definition TProcura.h:888
TParametro inst
Definition TProcura.h:883
virtual void MostrarSolucao()
definir para visualizar a solução
static bool memoriaEsgotada
Flag indicando problemas de memória esgotada.
Definition TProcura.h:583
static void MostraCaixa(TVector< TString > titulo, ECaixaParte parte, TVector< int > largura, bool aberta=true, int identacao=0)
Definition TProcura.cpp:372
virtual void Debug(bool completo=true)
Mostra o estado no ecrã, para debug.
Definition TProcura.cpp:89
int Parametro(int id) const
Definition TProcura.h:610
virtual int ExecutaAlgoritmo()
Executa o algoritmo com os parametros atuais.
Definition TProcura.h:262
static int Dominio(int &variavel, int min=INT_MIN, int max=INT_MAX)
Limita o domínio de um parâmetro inteiro.
virtual void RelatorioValidacao(TVector< TResultado > resultados, TVector< int > referencias)
Definition TProcura.cpp:296
static int modoMPI
Modo MPI.
Definition TProcura.h:590
virtual void Inicializar(void)
Coloca o objeto no estado inicial da procura.
Definition TProcura.h:248
static TVector< TVector< int > > configuracoes
Conjuntos de configurações para teste empírico.
Definition TProcura.h:573
static int resultado
Resultado retornado pelo algoritmo na última execução.
Definition TProcura.h:575
void BarraTorneio(bool nomes)
Mostra a barra de progresso ou nomes do torneio.
void ExecutaTarefa(TVector< TResultado > &resultados, int inst, int conf)
Executa uma tarefa num teste empírico.
void AjudaUtilizacao(TString programa)
Mostra ajuda de utilização do programa.
void MostrarTorneio(TVector< TVector< int > > &torneio, bool jogo=false)
Mostra os resultados do torneio.
static TString ficheiroInstancia
prefixo do nome do ficheiro de uma instância - editado pelo utilizador Caso não seja nulo,...
Definition TProcura.h:562
static int mpiID
MPI - rank do processo.
Definition TProcura.h:585
void MostrarConfiguracoes(int detalhe, int atual=-1)
Mostra as configurações disponíveis.
Definition TProcura.cpp:977
void MostraParametros(int detalhe=1, TVector< int > *idParametros=NULL, TString titulo="")
Mostra os parâmetros atuais.
Definition TProcura.cpp:557
virtual int64_t Indicador(int id)
Retorna um indicador, após a execução do algoritmo.
Definition TProcura.cpp:76
virtual TVector< TString > Solucao()
retorna uma solução no formato do TResultado, para ser gravada em ficheiro de soluções,...
Definition TProcura.h:553
static TString NovoTexto(TString prompt)
static int gravarSolucao
Gravar solução CSV (todas as ações)
Definition TProcura.h:593
static TString ficheiroGravar
prefixo do nome do ficheiro para gravar a instância para ficheiro (terá sido gerada)
Definition TProcura.h:564
TVector< TResultado > ExtrairConfiguracao(TVector< TResultado > &resultados, int configuracao)
Extrai resultados de uma determinada configuração.
virtual void Gravar(void)
Definition TProcura.h:251
void TesteInicio(TVector< int > &instancias, TVector< int > &configAtual)
arranque de teste, auxiliar aos Testes Empíricos
static int iteracoes
Número total de iterações realizadas na última execução.
Definition TProcura.h:579
static void FinalizaMPI()
Finaliza o ambiente MPI, se aplicável.
virtual void TesteValidacao(TVector< int > instancias, TVector< int > impossiveis, TVector< int > referencias, TString fichSolucoes, TString fichResultados="")
Executa testes de validação, executando cada solução na instância respetiva, e verificando a sua vali...
Definition TProcura.cpp:163
int NovaConfiguracao(TVector< int > &parametros)
Adiciona uma nova configuração se ainda não existir.
Definition TProcura.cpp:736
static TString MostraInt(int64_t valor, bool cor=true)
Mostra inteiro grande num formato humano.
Definition TProcura.cpp:792
TVector< int > SolicitaInstancias()
Solicita ao utilizador uma lista de instâncias.
Definition TProcura.cpp:844
virtual void ResetParametros()
Inicializa os parâmetros, indicadores e instâncias.
Definition TProcura.cpp:51
virtual void TesteEmpiricoTrabalhador(TVector< int > instancias, TString ficheiro="")
Teste empírico com modo mestre-escravo (este é o escravo)
virtual bool Parar(void)
Verifica se a procura deve ser interrompida.
Definition TProcura.h:405
bool JuntarCSV(TString ficheiro)
Juntar ficheiros CSV gerados por diferentes processos MPI em um único ficheiro.
static int NovoValor(TString prompt)
bool ParametroAtivo(int id, TVector< int > *valores=NULL) const
Definition TProcura.h:612
virtual void TesteManual(TString nome)
Inicializa a interação com o utilizador.
Definition TProcura.cpp:106
static TString MostraTempo(double segundos)
Mostra tempo num formato humano.
Definition TProcura.cpp:765
virtual void ExecucaoTerminada()
Chamar após a execução do algoritmo. Grava o tempo consumido.
virtual void main(int argc, char *argv[], TString nome)
Inicializa a interação com o utilizador.
void MostraRelatorio(TVector< TResultado > &resultados, bool ultimo=false)
Mostra um relatório dos resultados.
bool EditarIndicadores()
Permite ao utilizador editar os indicadores a utilizar.
Definition TProcura.cpp:652
static int mpiCount
MPI - número de processos.
Definition TProcura.h:587
static void InicializaMPI(int argc, char *argv[])
Inicializa o ambiente MPI, se aplicável.
void MostraIndicadores()
Mostra os indicadores definidos.
static void Mensagem(TString titulo, const char *fmt,...)
Definition TProcura.cpp:506
static TVector< TIndicador > indicador
Indicadores que podem ser calculados após a execução, quer com informação da instância,...
Definition TProcura.h:570
virtual void TesteEmpiricoGestor(TVector< int > instancias, TString ficheiro="")
Teste empírico com modo mestre-escravo (este é o mestre)
void DebugTabela(ENivelDebug nivel, TVector< int >tabela, TString tipo="", TString prefixo="", int modoCor=0, bool duplaColuna=false)
Mostra uma tabela de inteiros, 10 elementos por linha, apenas se o nível de debug for igual ou superi...
bool RelatorioCSV(TVector< TResultado > &resultados, TString ficheiro, bool parametros=true)
Gera um relatório CSV com os resultados.
virtual bool Validar(TVector< TString > solucao)
Verifica a validade de uma solução para a instância atual.
Definition TProcura.h:481
void TesteFim()
static clock_t instanteFinal
Instante final (deadline) da corrida atual.
Definition TProcura.h:581
void EditarParametros()
Permite ao utilizador editar os parâmetros.
Definition TProcura.cpp:687
void InserirConfiguracoes(TString str, TVector< int > &base)
Insere configurações a partir de uma string.
Definition TProcura.cpp:905
int MelhorResultado(TResultado base, TResultado alternativa)
Compara dois resultados para determinar o melhor.
virtual void Explorar()
definir para explorar manualmente os dados (não definido em TProcura, apenas em TProcuraConstrutiva)
Definition TProcura.h:547
static void DebugHSL(float h=-1, float s=1.0, float l=0.2, bool fundo=true)
Muda a cor (fundo/letra) com HSL.
Definition TProcura.cpp:529
void CalculaTorneio(TVector< TResultado > &resultados)
Calcula o torneio entre várias configurações.
void ConfiguracaoAtual(TVector< int > &parametros, int operacao)
Grava ou lê a configuração atual.
Definition TProcura.cpp:753
static TVector< int > indAtivo
Definition TProcura.h:571
static TParametro instancia
ID da instância atual, a ser utilizado em SolucaoVazia().
Definition TProcura.h:22
virtual TVector< int64_t > CodificarSolucao()
retorna um vetor de inteiros com a codificação da solução (esta codificação será adicionada aos indic...
Definition TProcura.h:551
static double Cronometro(enum ECronometro id=CONT_ALGORITMO, bool inicializar=false)
retorna o tempo em segundos desde que o cronómetro foi inicializado
Definition TProcura.h:859
void InserirRegisto(TVector< TResultado > &resultados, int inst, int conf)
Insere um novo registo de resultados.
Definition TProcura.cpp:821
virtual void TesteEmpirico(TVector< int > instancias, TString ficheiro="")
Executa testes empíricos, em todas as configurações guardadas, nas instâncias selecionadas.
static TVector< TParametro > parametro
Parâmetros a serem utilizados na configuração atual.
Definition TProcura.h:567
void EditarConfiguracoes()
Permite ao utilizador editar as configurações.
Definition TProcura.cpp:860
int64_t Registo(TResultado &resultado, int id)
Procura um registo com determinado id.
Definition TProcura.cpp:831
virtual void LimparEstatisticas()
Chamar antes da execução do algoritmo. Limpa valores estatísticos, e fixa o instante limite de tempo ...
Definition TProcura.cpp:95
void SolicitaInstancia()
Solicita ao utilizador o ID da instância a utilizar, permitindo alterar também o prefixo do ficheiro.
static int modoKMGT
Modo KMGT - Kilo, Mega, Giga, Tera.
Definition TProcura.h:596
static double tempo
tempo consumido na última execução.
Definition TProcura.h:577
bool TempoExcedido()
Definition TProcura.h:600
static void MostraConjunto(TVector< int > valores, const char *etiqueta)
TVector< TString > tok(const char *delim=" \t\n\r") const
Definition TVector.h:1196
TString & printf(const char *fmt,...)
Definition TVector.h:1150
bool Empty() const
Definition TVector.h:1184
TVector< TString > readLines()
Definition TVector.h:1229
TString & writeLines(const TVector< TString > &lines, bool append=false)
Definition TVector.h:1249
Item & First()
Definition TVector.h:224
Item & Last()
Definition TVector.h:227
TVector< Item > & Delete(int i)
Remove o elemento na posição i deslocando os seguintes.
Definition TVector.h:826
int Count() const
Definition TVector.h:230
Item * Data()
Acesso direto.
Definition TVector.h:219
virtual bool Empty() const
Definition TVector.h:233
int ContaUTF8(const char *str)
Definition compact.h:50
void init_io()
Definition compact.h:38
Estrutura para registo de um parâmetro.
Definition TProcura.h:175
int valor
valor do parâmetro
Definition TProcura.h:179
int max
valor máximo que o parâmetro pode tomar
Definition TProcura.h:183
int min
valor mínimo que o parÂmetro pode tomar
Definition TProcura.h:181