VecDeque e BinaryHeap em Rust: filas e filas de prioridade
Pense na fila de uma padaria. Quem chega entra no fim, e o atendente chama quem está na frente. Agora pense na sala de espera de um pronto-socorro. Lá, o paciente mais grave é atendido primeiro, não importa a hora em que chegou. As duas são “filas”, mas seguem regras diferentes.
O Rust tem uma coleção para cada uma. Um VecDeque é uma fila rápida em que você pode colocar e tirar itens nas duas pontas. Um BinaryHeap é uma fila de prioridade: ele sempre entrega o maior (ou mais urgente) item primeiro. Você já conhece os vetores, e a página de HashSet já mostrou o VecDeque por cima. Esta página vai mais fundo e ainda apresenta o BinaryHeap.
Por que um Vec é uma fila ruim
Seção intitulada “Por que um Vec é uma fila ruim”Você pode usar um Vec como fila. Coloca com push e tira da frente com remove(0). Funciona. O problema é o que acontece por trás.
Um Vec guarda os itens todos juntinhos, a partir da posição 0. Quando você remove o primeiro item, todos os outros precisam andar uma casa para a esquerda para fechar o buraco. Com 3 itens, ninguém percebe. Com 100.000 itens, cada remove(0) move 99.999 valores. É isso que “O(n)” quer dizer: o trabalho cresce junto com o tamanho da lista.
O VecDeque (pronuncia-se “vec dek”, abreviação de double-ended queue, ou fila de duas pontas) resolve isso com um ring buffer (buffer circular). Imagine uma mesa redonda com cadeiras numeradas e uma plaquinha dizendo “a fila começa aqui”. Quando a primeira pessoa sai, ninguém se levanta. Você só passa a plaquinha para a cadeira seguinte. Quando alguém novo chega e a última cadeira está ocupada, ele senta na cadeira 0, se ela estiver livre, dando a volta na mesa. Colocar ou tirar em qualquer ponta é só “mudar a plaquinha” ou “ocupar uma cadeira”, então leva o mesmo tempinho, não importa o tamanho da fila. Isso é “O(1)”.
use std::collections::VecDeque;
fn main() { // Um Vec usado como fila: funciona, mas remove(0) empurra todo mundo para a esquerda. let mut vec_line = vec!["Ana", "Ben", "Cai"]; let served = vec_line.remove(0); println!("Vec atendeu {served}, ainda esperando: {:?}", vec_line);
// Um VecDeque: pop_front só move a marcação de "início". let mut deque_line = VecDeque::from(vec!["Ana", "Ben", "Cai"]); let served = deque_line.pop_front(); println!("VecDeque atendeu {:?}, ainda esperando: {:?}", served, deque_line);}Saída:
Vec atendeu Ana, ainda esperando: ["Ben", "Cai"]VecDeque atendeu Some("Ana"), ainda esperando: ["Ben", "Cai"]O resultado parece igual. A diferença está na velocidade, e ela só aparece quando a fila fica longa. Repare em mais uma coisa: pop_front retorna um Option. Se a fila estiver vazia, você recebe None em vez de o programa quebrar.
O básico do VecDeque
Seção intitulada “O básico do VecDeque”O VecDeque fica em std::collections, então você precisa de uma linha use. Depois disso, os métodos são bem diretos: push (colocar) ou pop (tirar), na frente (front) ou atrás (back).
use std::collections::VecDeque;
fn main() { let mut deque: VecDeque<i32> = VecDeque::new(); deque.push_back(2); deque.push_back(3); deque.push_front(1); println!("{:?}", deque);
println!("frente: {:?}, fim: {:?}", deque.front(), deque.back()); println!("tamanho: {}, vazio: {}", deque.len(), deque.is_empty()); println!("deque[1] = {}", deque[1]); println!("get(9) = {:?}", deque.get(9));
let first = deque.pop_front(); let last = deque.pop_back(); println!("tirei {:?} e {:?}, sobrou: {:?}", first, last, deque);}Saída:
[1, 2, 3]frente: Some(1), fim: Some(3)tamanho: 3, vazio: falsedeque[1] = 2get(9) = Nonetirei Some(1) e Some(3), sobrou: [2]front e back deixam você olhar sem remover nada. Acessar por índice com deque[1] funciona igualzinho a um Vec, e dá panic se o índice for grande demais. Por isso, use get quando não tiver certeza.
Você também pode criar um VecDeque a partir de um Vec, percorrê-lo com um loop e transformá-lo de volta em Vec ou em slice quando outra função precisar:
use std::collections::VecDeque;
fn main() { let mut playlist = VecDeque::from(vec!["Intro", "Verso", "Refrão"]); playlist.push_front("Contagem"); playlist.push_back("Final");
for (i, song) in playlist.iter().enumerate() { println!("{}. {}", i + 1, song); }
// Precisa de uma slice normal? make_contiguous coloca tudo em sequência na memória. let songs = playlist.make_contiguous(); songs.sort(); println!("ordenado: {:?}", songs);
// Ou transforme tudo de volta em um Vec. let as_vec: Vec<&str> = playlist.into(); println!("como Vec: {:?}", as_vec);}Saída:
1. Contagem2. Intro3. Verso4. Refrão5. Finalordenado: ["Contagem", "Final", "Intro", "Refrão", "Verso"]como Vec: ["Contagem", "Final", "Intro", "Refrão", "Verso"]Por que o make_contiguous é necessário? Lembre da mesa redonda: depois de algumas voltas, os itens podem estar em dois pedaços separados da memória. O make_contiguous junta tudo em um pedaço só e entrega uma slice normal, para você poder usar ferramentas de slice como o sort. Você não vai precisar disso com frequência, mas é bom saber que existe.
Usos reais do VecDeque
Seção intitulada “Usos reais do VecDeque”Aqui estão três tarefas do dia a dia em que um VecDeque encaixa perfeitamente.
Uma fila de tarefas “primeiro a entrar, primeiro a sair”. Os trabalhos de impressão esperam na fila. Os novos vão para o fim, e a impressora pega da frente. Um trabalho urgente ainda pode furar a fila com push_front:
use std::collections::VecDeque;
fn main() { let mut print_queue = VecDeque::new(); print_queue.push_back("relatorio.pdf"); print_queue.push_back("foto.jpg"); print_queue.push_back("fatura.pdf");
// Urgente! Este vai direto para a frente da fila. print_queue.push_front("cartao-de-embarque.pdf");
while let Some(job) = print_queue.pop_front() { println!("Imprimindo {job} ({} ainda esperando)", print_queue.len()); } println!("Tudo pronto. Fila vazia: {}", print_queue.is_empty());}Saída:
Imprimindo cartao-de-embarque.pdf (3 ainda esperando)Imprimindo relatorio.pdf (2 ainda esperando)Imprimindo foto.jpg (1 ainda esperando)Imprimindo fatura.pdf (0 ainda esperando)Tudo pronto. Fila vazia: truewhile let Some(job) = ...pop_front() é o loop clássico de fila. Ele continua até pop_front retornar None, o que significa que a fila esvaziou. (Veja if let se esse formato for novidade para você.)
Um histórico dos “últimos N itens”. Uma caixa de busca que lembra suas 3 pesquisas mais recentes. Quando ela enche, a mais antiga cai pela frente:
use std::collections::VecDeque;
const MAX_RECENT: usize = 3;
fn remember(recent: &mut VecDeque<String>, search: &str) { if recent.len() == MAX_RECENT { recent.pop_front(); // esquece a pesquisa mais antiga } recent.push_back(search.to_string());}
fn main() { let mut recent = VecDeque::with_capacity(MAX_RECENT);
for search in ["livro de rust", "vecdeque", "binary heap", "pizza perto de mim"] { remember(&mut recent, search); println!("{:?}", recent); }
// Mostra a mais nova primeiro, como uma caixa de busca faria. let newest_first: Vec<&String> = recent.iter().rev().collect(); println!("mais novas primeiro: {:?}", newest_first);}Saída:
["livro de rust"]["livro de rust", "vecdeque"]["livro de rust", "vecdeque", "binary heap"]["vecdeque", "binary heap", "pizza perto de mim"]mais novas primeiro: ["pizza perto de mim", "binary heap", "vecdeque"]Uma média em janela deslizante. Um termômetro manda uma leitura por minuto, e você quer a média das 3 últimas. Cada leitura nova entra no fim, e a mais antiga sai pela frente. Manter uma soma corrente (sum) evita que você tenha que somar a janela inteira de novo a cada passo:
use std::collections::VecDeque;
fn main() { let readings = [20.0, 22.0, 24.0, 23.0, 19.0, 21.0]; let window_size = 3; let mut window: VecDeque<f64> = VecDeque::new(); let mut sum = 0.0;
for reading in readings { window.push_back(reading); sum += reading;
if window.len() > window_size { sum -= window.pop_front().unwrap(); // a leitura mais antiga sai }
if window.len() == window_size { let average = sum / window_size as f64; println!("{:?} -> média {:.1}", window, average); } }}Saída:
[20.0, 22.0, 24.0] -> média 22.0[22.0, 24.0, 23.0] -> média 23.0[24.0, 23.0, 19.0] -> média 22.0[23.0, 19.0, 21.0] -> média 21.0O unwrap() é seguro aqui: só tiramos um item quando a janela tem mais de 3, então ela não pode estar vazia.
BinaryHeap: sempre o maior primeiro
Seção intitulada “BinaryHeap: sempre o maior primeiro”Agora, a sala de espera do pronto-socorro. Às vezes você não quer saber quem chegou primeiro. Você quer “me dê a coisa mais importante agora”, de novo e de novo, enquanto coisas novas continuam chegando.
Você poderia jogar tudo em um Vec e ordenar a cada vez. Mas ordenar tudo só para achar o item do topo é muito trabalho jogado fora. Um BinaryHeap mantém os itens organizados só mais ou menos, o suficiente para que o maior esteja sempre no topo. Colocar um item ou tirar o do topo é barato (O(log n), que cresce bem devagar: uns 17 passos para 100.000 itens).
O BinaryHeap do Rust é um max-heap: pop sempre devolve o maior valor. Aqui estão os lances de um leilão:
use std::collections::BinaryHeap;
fn main() { let mut bids = BinaryHeap::new(); bids.push(120); bids.push(450); bids.push(80); bids.push(300);
println!("maior lance: {:?}", bids.peek()); println!("número de lances: {}", bids.len());
while let Some(bid) = bids.pop() { println!("próximo: {bid}"); }
let heap = BinaryHeap::from(vec![5, 1, 4, 2, 3]); println!("ordenado: {:?}", heap.into_sorted_vec());}Saída:
maior lance: Some(450)número de lances: 4próximo: 450próximo: 300próximo: 120próximo: 80ordenado: [1, 2, 3, 4, 5]peek olha o topo sem remover. pop remove. into_sorted_vec consome o heap e devolve um Vec ordenado do menor para o maior. Assim como no VecDeque, tanto peek quanto pop retornam um Option, então um heap vazio nunca derruba o programa.
Min-heaps com Reverse
Seção intitulada “Min-heaps com Reverse”E se você quiser o menor primeiro? A parada de entrega mais próxima, o voo mais barato, o prazo mais curto. Você não precisa de outra coleção. Embrulhe cada valor em std::cmp::Reverse, que simplesmente inverte a comparação: o que era o maior agora é o menor.
use std::cmp::Reverse;use std::collections::BinaryHeap;
fn main() { // Paradas de entrega, por distância em km. Sempre vá para a mais próxima. let mut stops = BinaryHeap::new(); for km in [12, 3, 8, 5] { stops.push(Reverse(km)); }
if let Some(Reverse(closest)) = stops.peek() { println!("parada mais próxima: {closest} km"); }
while let Some(Reverse(km)) = stops.pop() { println!("dirija {km} km"); }}Saída:
parada mais próxima: 3 kmdirija 3 kmdirija 5 kmdirija 8 kmdirija 12 kmReverse(km) é uma struct bem pequena que só embrulha o valor. O pattern Some(Reverse(km)) desembrulha as duas camadas de uma vez, e você recebe o número puro de volta.
Uma fila de prioridade de structs
Seção intitulada “Uma fila de prioridade de structs”Tarefas de verdade não são só números. Elas têm um nome, uma prioridade, talvez uma hora de chegada. Para colocar sua própria struct em um BinaryHeap, o Rust precisa saber comparar duas delas, ou seja, a struct precisa implementar Ord (veja Traits comuns).
O jeito mais rápido é o derive. Uma coisa importante: o derive compara os campos de cima para baixo. Ele compara o primeiro campo e só olha o segundo se os primeiros forem iguais. Então coloque primeiro o campo pelo qual você quer ordenar:
use std::collections::BinaryHeap;
// A ordem dos campos importa: o derive compara `priority` primeiro, depois `name`.#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]struct Task { priority: u8, name: String,}
fn main() { let mut todo = BinaryHeap::new(); todo.push(Task { priority: 2, name: "marcar dentista".to_string() }); todo.push(Task { priority: 5, name: "consertar o servidor fora do ar".to_string() }); todo.push(Task { priority: 1, name: "regar as plantas".to_string() }); todo.push(Task { priority: 2, name: "responder e-mail".to_string() });
while let Some(task) = todo.pop() { println!("[{}] {}", task.priority, task.name); }}Saída:
[5] consertar o servidor fora do ar[2] responder e-mail[2] marcar dentista[1] regar as plantasSe você trocasse os dois campos de lugar e name viesse primeiro, o heap ordenaria as tarefas em ordem alfabética e quase ignoraria a prioridade. Erro fácil de cometer, bug silencioso.
Olhe as duas tarefas com prioridade 2. “marcar dentista” chegou antes, mas “responder e-mail” saiu primeiro. O empate foi decidido pelo name, e “responder” ganha de “marcar” porque vem depois no alfabeto. É previsível, mas provavelmente não é o que você quer. Normalmente, no empate, a tarefa que chegou primeiro deve sair primeiro. Para uma regra personalizada assim, escreva o Ord você mesmo:
use std::cmp::Ordering;use std::collections::BinaryHeap;
#[derive(Debug, PartialEq, Eq)]struct Job { priority: u8, id: u32, // ordem de chegada: um id menor chegou antes name: String,}
impl Ord for Job { fn cmp(&self, other: &Self) -> Ordering { self.priority .cmp(&other.priority) // a prioridade maior ganha .then_with(|| other.id.cmp(&self.id)) // no empate, ganha o id mais antigo }}
impl PartialOrd for Job { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }}
fn main() { let mut jobs = BinaryHeap::new(); jobs.push(Job { priority: 2, id: 1, name: "marcar dentista".to_string() }); jobs.push(Job { priority: 5, id: 2, name: "consertar o servidor fora do ar".to_string() }); jobs.push(Job { priority: 1, id: 3, name: "regar as plantas".to_string() }); jobs.push(Job { priority: 2, id: 4, name: "responder e-mail".to_string() });
while let Some(job) = jobs.pop() { println!("[{}] #{} {}", job.priority, job.id, job.name); }}Saída:
[5] #2 consertar o servidor fora do ar[2] #1 marcar dentista[2] #4 responder e-mail[1] #3 regar as plantasO truque está no then_with: se as prioridades forem iguais, os ids são comparados ao contrário (other.id contra self.id). Isso faz o id menor contar como “maior”, então ele sai primeiro do max-heap. Não se preocupe se isso levar um minutinho para fazer sentido. Inverter a comparação é exatamente o que o Reverse faz por você, só que escrito à mão. O bloco do PartialOrd é sempre o mesmo código padrão: ele só chama o seu cmp.
Um algoritmo de verdade: as 3 maiores pontuações
Seção intitulada “Um algoritmo de verdade: as 3 maiores pontuações”Digamos que você tenha uma lista enorme de pontuações de um jogo e só queira as 3 maiores. Ordenar a lista inteira funciona, mas existe um truque esperto que nunca guarda mais do que alguns itens.
Use um min-heap de tamanho 3. Coloque cada pontuação. Quando o heap ficar com 4 itens, tire um. Como é um min-heap, o que você joga fora é o menor. No final, só sobram as 3 melhores.
use std::cmp::Reverse;use std::collections::BinaryHeap;
fn main() { let scores = [72, 95, 64, 88, 91, 55, 99, 80]; let mut top = BinaryHeap::new(); // um min-heap: a menor pontuação fica no topo
for score in scores { top.push(Reverse(score)); if top.len() > 3 { top.pop(); // quatro é um a mais: descarta a menor } }
let best: Vec<u32> = top .into_sorted_vec() .into_iter() .map(|Reverse(score)| score) .collect(); println!("top 3: {:?}", best);}Saída:
top 3: [99, 95, 91]Um bônus legal: into_sorted_vec ordena os valores Reverse do “menor” para o “maior”, o que, para as pontuações reais, significa do maior para o menor. Então a melhor pontuação sai primeiro, sem nenhuma ordenação extra. Esse padrão funciona com uma lista de um milhão de pontuações guardando só 4 na memória.
Qual devo usar?
Seção intitulada “Qual devo usar?”Aqui vai uma colinha. “Rápido” quer dizer que o tempo fica mais ou menos o mesmo, não importa o tamanho da coleção. “Lento” quer dizer que ele cresce junto com o tamanho.
| Coleção | Ótima para | Colocar | Tirar da frente / do topo | Achar um item qualquer |
|---|---|---|---|---|
Vec<T> |
Uma lista lida por posição; uma pilha | Rápido no fim | Lento (remove(0) empurra tudo) |
Lento (olha item por item) |
VecDeque<T> |
Filas: colocar e tirar nas duas pontas | Rápido nas duas pontas | Rápido nas duas pontas | Lento (olha item por item) |
BinaryHeap<T> |
“Me dê o maior / mais urgente agora” | Quase rápido (O(log n)) | Quase rápido: sempre o maior | Lento, e sem ordem definida |
BTreeSet<T> |
Valores únicos, sempre ordenados | Quase rápido (O(log n)) | Quase rápido: o menor e o maior | Quase rápido (O(log n)) |
Na dúvida, comece com um Vec. Parta para o VecDeque quando você tira coisas da frente. Parta para o BinaryHeap quando você vive perguntando “o que é mais importante agora?”. Use um BTreeSet quando precisar de valores únicos e ordenados e quiser procurar itens, não só pegar o do topo.
Erros comuns
Seção intitulada “Erros comuns”Achar que .iter() em um BinaryHeap vem ordenado. Um heap é organizado só mais ou menos. O maior item vem primeiro, mas o resto fica numa ordem bagunçada que é útil para o heap, não para você:
use std::collections::BinaryHeap;
fn main() { let heap = BinaryHeap::from(vec![1, 5, 2, 8, 3, 9]);
println!("iter: {:?}", heap.iter().collect::<Vec<_>>()); println!("ordenado: {:?}", heap.clone().into_sorted_vec());
let mut heap = heap; print!("retirados:"); while let Some(n) = heap.pop() { print!(" {n}"); } println!();}Saída:
iter: [9, 8, 2, 5, 3, 1]ordenado: [1, 2, 3, 5, 8, 9]retirados: 9 8 5 3 2 1O mesmo vale para imprimir um heap com {:?}: você vai ver a bagunça interna. Se precisar dos itens em ordem, tire um por um com pop ou chame into_sorted_vec.
Colocar uma struct sem Ord em um BinaryHeap. O heap precisa comparar os itens para saber qual vai para o topo. Se a sua struct não pode ser comparada, o Rust barra você:
use std::collections::BinaryHeap;
struct Ticket { urgency: u8, title: String,}
fn main() { let mut tickets = BinaryHeap::new(); tickets.push(Ticket { urgency: 3, title: "login quebrado".to_string() });}error[E0277]: the trait bound `Ticket: Ord` is not satisfied --> src/main.rs:10:13 |10 | tickets.push(Ticket { urgency: 3, title: "login quebrado".to_string() }); | ^^^^ the trait `Ord` is not implemented for `Ticket` |help: consider annotating `Ticket` with `#[derive(Ord)]`A mensagem diz que Ticket não implementa a trait Ord, e sugere adicionar #[derive(Ord)]. A correção é colocar #[derive(PartialEq, Eq, PartialOrd, Ord)] acima da struct (o Ord também precisa das outras três), com urgency como primeiro campo. Ou escreva o Ord à mão, como no exemplo do Job acima.
Prática
Seção intitulada “Prática”1. Cada um na sua vez
Seção intitulada “1. Cada um na sua vez”Três amigos, Ana, Ben e Cai, se revezam em um jogo de tabuleiro. Coloque os três em um VecDeque e imprima quem joga nas rodadas de 1 a 5. Depois de cada rodada, o jogador vai para o fim da fila. Dica: tire o jogador com pop_front, imprima e depois coloque o mesmo jogador de volta com push_back.
Ver solução
use std::collections::VecDeque;
fn main() { let mut players = VecDeque::from(vec!["Ana", "Ben", "Cai"]);
for turn in 1..=5 { let player = players.pop_front().unwrap(); println!("Rodada {turn}: {player}"); players.push_back(player); // de volta para o fim da fila }}Saída:
Rodada 1: AnaRodada 2: BenRodada 3: CaiRodada 4: AnaRodada 5: BenA frente e o fim são rápidos em um VecDeque, então girar a fila quase não custa nada, não importa quantos jogadores existam.
2. O pronto-socorro
Seção intitulada “2. O pronto-socorro”Os pacientes chegam com uma gravidade de 1 a 5. O mais grave é atendido primeiro. Se dois pacientes tiverem a mesma gravidade, quem chegou antes é atendido antes. Use um BinaryHeap de tuplas e imprima a ordem de atendimento. Pacientes: Mia (gravidade 2, chegou em 1º), Leo (5, 2º), Sam (2, 3º), Zoe (4, 4º). Dica: tuplas são comparadas campo por campo, igualzinho a uma struct com derive. Experimente (severity, Reverse(arrival), name).
Ver solução
use std::cmp::Reverse;use std::collections::BinaryHeap;
fn main() { // (gravidade, Reverse(chegada), nome) let mut waiting = BinaryHeap::new(); waiting.push((2, Reverse(1), "Mia")); waiting.push((5, Reverse(2), "Leo")); waiting.push((2, Reverse(3), "Sam")); waiting.push((4, Reverse(4), "Zoe"));
while let Some((severity, Reverse(arrival), name)) = waiting.pop() { println!("{name} (gravidade {severity}, chegou em #{arrival})"); }}Saída:
Leo (gravidade 5, chegou em #2)Zoe (gravidade 4, chegou em #4)Mia (gravidade 2, chegou em #1)Sam (gravidade 2, chegou em #3)A gravidade é comparada primeiro, então o Leo vai primeiro. Mia e Sam empatam na gravidade, então o Reverse(arrival) decide, e quem chegou antes (Mia) ganha. Como cada número de chegada é diferente, o nome nunca é usado para desempatar.
- Um
Vecé uma fila ruim:remove(0)empurra todos os outros itens. UmVecDequeé um ring buffer (buffer circular) compush/poprápidos nas duas pontas. - Use
VecDequepara filas “primeiro a entrar, primeiro a sair”, históricos dos “últimos N itens” e janelas deslizantes. - Um
BinaryHeapé um max-heap:popepeeksempre devolvem o maior item, sem ordenar tudo. - Embrulhe os valores em
std::cmp::Reversepara ter um min-heap (o menor primeiro). - Structs em um heap precisam de
Ord. Comderive, os campos são comparados de cima para baixo, então coloque a prioridade primeiro. Escreva oOrdà mão para desempates personalizados. .iter()em um heap não vem ordenado. Tire os itens um por um ou useinto_sorted_vec.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Por que VecDeque::pop_front é mais rápido que Vec::remove(0) em uma lista longa?
2. Você coloca 4, 9 e 2 em um BinaryHeap. O que o primeiro pop() retorna?
3. Como fazer um BinaryHeap devolver o menor valor primeiro?
4. Uma struct deriva Ord com os campos name e depois priority. O que decide a ordem em um BinaryHeap?
Pontuação: 0 / 4
Para saber mais
Seção intitulada “Para saber mais”std::collections— documentação da biblioteca padrão (em inglês): o guia oficial “qual coleção devo usar?”, com uma tabela de velocidade para cada coleção.VecDeque— documentação da biblioteca padrão (em inglês): todos os métodos, incluindorotate_left,range,retaineas_slices.BinaryHeap— documentação da biblioteca padrão (em inglês): todos os métodos do heap, além de exemplos de min-heaps comReversee depeek_mut.Reverse— documentação da biblioteca padrão (em inglês): o pequeno invólucro que inverte qualquer comparação, com exemplos de ordenação ao contrário.- Common Collections — The Rust Book (em inglês): o capítulo do Book sobre vetores, strings e hash maps, as coleções que você vai usar junto com estas.