quinta-feira, 10 de abril de 2025

Para quem queira saber quando o SSD está falhando

Recebi ontem um alarme do S.M.AR.T. informando que o meu SSD está falhando depois de cerca de 10 anos de muito uso. O Swap e o /tmp estavam nele, além do /usr, e neste tempo compilei muitos programas nele e o sistema algumas vezes.

segunda-feira, 10 de outubro de 2022

Como fazer algo de forma errada, e aprendendo a certo

Algum tempo atrás eu vi um código muito errado, mas muito errado mesmo, e de muitas formas diferentes, que me deixou chocado.

Não vou colocar o código original aqui, mas vou refazer usando o mesmo algoritmo usando outra situação.

Situação nova: Em alguns países o ponto é usado para separar as decimais e a vírgula é usada a cada 3 casas não decimais, mas em outros, como o Brasil, é ao contrário.

Por exemplo, nos EUA a velocidade da luz seria escrita como 1,079,252,848.8 Km/h (Eu sei, eles não usam Km/h, e sim aquela medida obsoleta de milhas por hora.), e no Brasil este número seria representado assim: 1.079.252.848,8 Km/h.

E no caso seria necessária uma função que fizesse esta troca e colocasse o "c = " na frente. A função seria escrita deste modo abaixo com o algoritmo que vi:

void Troca( char *texto )
{
    char temp[50] = "c = " ;
    int i ;

    for( i = 0 ; i < 50 ; i++ )
        if( texto[i] == ',')
            texto[i] = '.' ;
        else if( texto[i] == '.')
            texto[i] = ',' ;
 
    strcat(temp,texto) ;
    strcpy(texto,temp) ;
}

Agora analise o código. Veja quantos erros tem nele. Imagine as consequências destes erros. Imagine como resolvê-los. Depois continue a ler.

quarta-feira, 14 de julho de 2021

Um pouco de a, b, c e main da programação modular.

Este é um mais um caso no qual uma explicação para uma pessoa nas redes sociais fica tão boa que vira um artigo no blog. O texto vai abaixo:
 
Um header, um arquivo .h, NUNCA deverá gerar código ou dados. Pode definir variáveis, mas somente como extern. Pode ter protótipos de funções, definição de tipos, enum, macros, mas NUNCA deve definir uma variável alocando espaço em memória ou ter o código de uma função.
 
Os .c e .cpp são onde estão as definições de variáveis e código.
 
Digamos que vamos fazer um programa que é dividido em partes. A parte "a" que é usada pela "b" e "c", e a parte "main" seria o programa principal e depende diretamente das partes "b" e "c".
 
Então teríamos os arquivos a.h, a.c, b.h, b.c, c.h, c.c e main.c.
 
a.c
 
Define algumas funções e variáveis que também serão vistas por outros módulos. Inclui alguns headers padrão entre <> e a.h entre aspas.
 
a.h
 
Contém os protótipos das funções escritas em a.c, e em extern as variáveis de a.c que precisam ser vistas por outros módulos.
 
b.c
 
Define algumas funções e variáveis que também serão vistas por outros módulos. Inclui alguns headers padrão entre <>, e a.h e b.h entre aspas.
 
b.h
 
Contém os protótipos das funções escritas em b.c, e em extern as variáveis de b.c que precisam ser vistas por outros módulos.
 
c.c
 
Define algumas funções e variáveis que também serão vistas por outros módulos. Inclui alguns headers padrão entre <>, e a.h e c.h entre aspas.
 
c.h
 
Contém os protótipos das funções escritas em c.c, e em extern as variáveis de c.c que precisam ser vistas por outros módulos.
 
main.c
 
Já que este depende diretamente somente de b.c e c.c, então inclui alguns headers padrão entre <>, e b.h e c.h entre aspas. A dependência do módulo "a" se faz indiretamente.
 
O Makefile deste projeto fica aproximadamente assim.
 
programa: main.c b.o c.o
    cc -s -O3 -o programa main.c a.o b.o c.o
 
a.o: a.c a.h
    cc -O3 -c a.c
 
b.o: b.h b.c a.o
    cc -O3 -c b.c
 
c.o: c.h c.c a.o
    cc -O3 -c c.c
 
E assim só os módulos necessários é que serão compilados. Se editar o main.c, só ele será compilado. Se editar o a.h ou o a.c, tudo será compilado, pois tudo depende diretamente ou indiretamente deles. Se editar b.c ou b.h, só b.c e main.c serão compilados.
 
Acho que dá para fazer um Makefile mais detalhado, de modo que nem main.c seja compilado do zero se os headers não forem modificados.

Aqui já terminou o texto da rede social, mas tenho este texto falando que vale para ler, pois justamente é sobre como variáveis são alocadas em memória, sobre extern, e sobre funções e variáveis locais a módulos.

sábado, 26 de setembro de 2020

Rodando vários navegadores e programas isolados por UIDs diferentes

O que vou ensinar aqui se aplica para todos (ou quase) os sistemas que rodam X Windows, ou seja, sistemas Unix-Like (FreeBSD, Linux etc), e VMS (Não uso um faz décadas.), e talvez mais alguns outros sistemas (Ouvi falar de X Windows em Mac OS X, que é um Unix-Like derivado do FreeBSD).

O X Windows é um sistema em rede, isto é, um programa em um computador pode se conectar ao ambiente gráfico de outro computador (ou terminal X Windows).

Como na época em que ele surgiu o poder computacional era muito menor do que o atual, surgiram terminais que rodavam o X Windows. Assim os programas rodariam em um grande computador e a interface gráfica dele estaria no terminal em separado.

Também existiam programas que transformavam PCs em terminais X Windows.

Aqui tem um ponto que dá nó na cabeça de muita gente. Quem roda o X Windows é o servidor, pois serve janelas, teclado, mouse etc, em suma, fornece uma interface com o usuário, para os programas que os requisitam, é que são clientes. Assim os computadores grandes, os mainframes, eram os clientes de X Windows e os terminais eram os servidores de X Windows

Assim o navegador (por exemplo) é um cliente de X Windows que se conecta a um servidor de X Windows, e como atualmente se tem muito mais poder computacional do que se tinha antigamente, ambos normalmente rodam no mesmo computador.

Quando eu fiz um curso de programação em X Windows em 1991 usávamos uma única DEC VAX Station que tínhamos disponível para toda a turma, e um DEC VAX por vários terminais seriais (acho que compatíveis com o DEC VT-100 conectados por RS232). Rodávamos os programas neste DEC VAX, mas pedíamos que as janelas fossem feitas an DEC VAX Station. Assim a turma toda poderia usar a estação gráfica, mas sem ninguém realmente sentar diante dela.

Aliás, uma das máquinas rodava DEC Ultrix como sistema operacional (acho que era a VAX Station, mas posso estar errado) e o outro rodava DEC VMS como sistema operacional. Ou seja, cliente e servidor não precisam usar o mesmo sistema operacional.

Mesmo o mundo tendo mudado muito, este recurso de rede do X Windows ainda existe, e ainda é usado, mas bem menos. E por questões de segurança, ele normalmente fica desativado.

Eu me lembro do X Windows suportar HDR naquela época (1991), mesmo com este termo ainda não existindo. Ele suportava 16 bits por cor, 48 bits por pixel, mesmo com quase todos os hardwares atuais (2020) não suportando isso. (Descobri o suporte a 16 bits por cor por pixel quando o meu degradê de cinza só mostrava preto. rsrs)

Eu uso este recurso do X Windows no meu computador, e separo um usuário para o jogo do Minetest. Aliás, agora tenho vários usuários para este jogo, já que criei o canal Não É Minecraft. Mas eu já fazia antes. Eu tenho um usuário que só uso uma vez por ano, pois é o usuário para fazer Imposto de Renda. Eu tenho um com o login do meu antigo trabalho, e assim mantinha um navegador com o Trello, o e-mail do trabalho etc, abertos completamente isolados dos pessoais.

Os usos para isto são muito amplos.

Outro uso que fiz foi quase 20 anos atrás. Eu pensava no reaproveitamento dos antigos 486 no tempo que eu trabalhei na Biblioteca Nacional. Eu rodei um Netscape (navegador web) no Pentium 4, que era a minha estação de trabalho, mas a janela era apresentada no 486 antigo que não tinha condições de rodar um navegador. A ideia era rodar os navegadores em um Pentium 4, e cada um deles fazendo a sua janela num 486 diferente. Em suma, eu estava transformando os 486 em terminais X Windows.

 

Agora vamos ao que interessa a muita gente

Para começar, o X Windows tem que ser iniciado com suporte à conexão remota. No meu sistema eu faço assim:

startx -- -listen tcp

Isto diz ao X Windows, em seu momento de inicialização, que ele tem que escutar a porta TCP dele, caso contrário ele não a abrirá (creio eu).

No seu sistema (se não for um FreeBSD) pode ser diferente. Se isto não funcionar no seu sistema pesquise é nele, e pode colocar nos comentários qual foi o sistema e qual foi a solução adotada.

E ainda tem que autorizar quem pode fazer requisições a ele, e isto é com o comando xhost, como abaixo:

xhost +localhost

Aqui estamos dizendo que é para aceitar todas as conexões da máquina local. Então qualquer usuário da máquina local pode fazer janelas nela, e não só o usuário que iniciou o X Windows na máquina.

Em uma janela shell, de linha de comando, pode-se fazer login num outro usuário, como o exemplo abaixo:

exec login fulano

E depois fornecer a devida senha. (Nota: fulano é o usuário no qual vai se fazer login neste exemplo.)

Podem consultar o manual do exec e do login para saber mais detalhes.

Agora tem que avisar aos programas que este usuário rodar qual é o display dele, onde tem que ser feitas as janelas e a interface com o usuário.

Se usa uma csh como shell de linha de comando, isto pode ser feito da forma abaixo.

setenv DISPLAY localhost:0.0

É basicamente colocar a informação numa variável do ambiente que os programas receberão.

Se sempre vai usar este usuário desta forma, sem ele realmente fazer login diretamente no computador, coloque a linha acima no arquivo .login que fará com que esta configuração sempre seja feita quando fizer o exec login citado acima.

Se usa uma Bourne Shell (sh) ou uma derivada (como a bash), faça da seguinte forma (Nota: Não testei, mas acho que deve funcionar.).

set DISPLAY='localhost:0.0'

export DISPLAY

E o arquivo de configuração será o .profile.

Podem perguntar e fazer acréscimos nos comentários.


terça-feira, 14 de janeiro de 2020

Preparando um novo HD no FreeBSD

Neste final de semana preparei um novo HD externo para funcionar no FreeBSD. Mas, mesmo sendo externo, quase tudo que vou falar aqui funciona para internos também.

Esta preparação incluiu testar o HD. Eu acho que antes de começar a usar um HD deve-se fazer testes nele. Eu falo por experiência, pois vi HDs novos com problemas, e que não passaram nos testes.

Aliás, parte do que vou falar aqui, especialmente o início, também vale para outros sistemas operacionais, especialmente os Unix Like. E estes testes podem ser feitos num sistema diferente do sistema final onde vai ser instalado o HD.

segunda-feira, 26 de agosto de 2019

Uma pequena vingança

Isto aconteceu por volta de 1991 na faculdade.

Um determinado computador num laboratório que eu frequentava tinha duas placas de vídeo, sendo que uma era especializada para tratamento de imagens. O monitor dela funcionava na mesma frequência das TVs, ou seja, 60 Hz na vertical e 15750 Hz na horizontal.

Em um dia uma garota que não sabia nada de eletrônica, de como um monitor funcionava etc, (não era culpa dela, pois era no Departamento de Geografia) estava usando este computador, mas eu sabia bem devido meus estudos de eletrônica.

Meu ouvido é (ou era) muito sensível a agudos, exceto o 15750 Hz e vizinhança próxima, pois toda TV faz este apito agudo. Eu escutava, mas não me incomodava.

Ela toda hora resetava o computador, o que fazia o segundo monitor sair de sincronismo na frequência horizontal. Isto me incomodava muito. Eu ficava escutando o barulho agudo fora de frequência certa.

Ela dava boot no computador, chamava o programa, carregava uma imagem, e aí tentava ajustar o monitor, e eu escutando o assovio por este tempo todo.

Aí começava outro martírio meu. Ela não virava o ajuste devagar. Ela ia rapidamente de ponta a ponta, fazendo toda a gama de assovios que aquele oscilador, com a alta tensão do monitor, eram capazes de fazer. E depois de várias tentativas, o comparador de fase do monitor conseguia travar na frequência certa. Mas o oscilador estava ajustado para operar naturalmente em outra frequência, pois dependia de onde ela parasse durante o ajuste.

Eu ainda tinha a certeza, devido aos meus conhecimentos de eletrônica, que ela estava fazendo errado. Isto também me incomodava.

Depois de um tempo de uso ela resetava o computador de novo. E começava tudo de novo. E de novo. E de novo. Etc.

Foram vários ciclos assim. Sei que tem gente que teria surtado, que teria passado bronca nela etc, mas usei outro método.

Em um dos resets que ela deu eu me levantei, fui até o monitor, e comecei a ajustar o monitor de ouvido até que chegasse o mais perto possível da frequência que deveria oscilar, 15750 Hz. Enquanto eu fazia isto ela falou:

- Espere eu colocar uma imagem.

Quando ela colocou a imagem o monitor funcionou direitinho. Em sincronismo. Ela ficou me olhando.

Ela devia estar pensando "Como eu tenho dificuldade de fazer isto com imagem, e ele fez sem imagem no monitor?".

Fiz um sinal do tipo "Ok" com a cabeça, e voltei para o meu canto da sala, onde eu estava trabalhando.

Como vingança, não contei como fiz isto.

Acho que eu tive a vantagem de perceber o que me incomodava, como me incomodava, porque me incomodava, e sabia como resolver. Nem todo mundo tem esta vantagem em muitas situações de incômodo. Acredito que inclusive eu em alguns casos.

domingo, 2 de junho de 2019

Ordem de testes

No outro dia me deparei com um código mais ou menos assim:

int f( bool a, int b, int c, int d )
{
  if( b > c && a == false )
    return 1 ;
  else if( c < d && a == false )
    return 2 ;
  else if( a == true )
    return 3 ;
  else
    return 4 ;
}


Olhem para ele e digam o que está errado, o que pode ser melhorado. Depois de pensar nele, cliquem em "Mais informações" para ver o resto da discussão.

quinta-feira, 7 de fevereiro de 2019

LEDs queimam? Normalmente não, mas...

Apesar de ter criado este blog para falar principalmente de computação, ele não é restrito a isto. Ele é para falar de nerdices, e como sou um nerd antigo, comecei pela eletrônica na minha adolescência, tal como muitos nerds da minha geração. Na década de 1980 ainda era caro ter um computador em casa.

Este texto é sobre LED (Light Emitting Diode - Diodo Emissor de Luz). Ele é um semicondutor que emite luz quando devidamente polarizado, com uma corrente passando por ele da forma direta. Como retificador ele é muito ruim, apesar de ser um diodo semicondutor. Mas é muito bom, eficiente e durável para pequenas fontes de luz. E nos últimos anos, com as melhorias tecnológicas, passaram a ser usados em sinais de trânsito (Uso que eu imaginava desde a década de 1980.), iluminação etc.

Uma das características dos LEDs é uma luz de espectro estreito, com uma cor bem definida, o que é bom para luzes indicadoras, sinais de trânsito, iluminações coloridas etc. LEDs brancos na realidade não existem. Já tratei sobre luz branca usando LEDs aqui.

Mas o assunto aqui é "LEDs queimam?".

quarta-feira, 22 de agosto de 2018

C: Imprimindo percentual

É muito comum uma saída com percentual, e com o símbolo '%' depois do número, mas a printf() usa este caractere para formatação de saída. Qual é a solução?

Já vi muitas vezes sendo resolvido assim:

printf( "%f %s\n",pecent,"%" );

Isto está errado, realmente muito errado. Mas por que está errado, e qual é a solução?

C: Ponteiros para função

Em C existe um recurso muito incomum. Ponteiros para funções. Não sei de outra linguagem que tenha isto, além de C e C++. Não sei se outras linguagens derivadas do C, como Java e C# tem isto (Podem responder nos comentários). Dá para fazer em Assembly, mas isto não conta.

Qual é o uso disto? Depende do caso. É possível passar uma função, aliás, o endereço de uma função, como parâmetro para outra função, para que ela a chame dentro dela, por exemplo.

Então vamos a um exemplo prático:

terça-feira, 17 de abril de 2018

Testando os quadrados por somas de ímpares de Pitágoras e otimização

Eu vi a seguinte publicação no Facebook e pensei em testar computacionalmente até um limite grande. Para quem não quer, ou não tem como, acessar o Facebook, reproduzo a imagem abaixo:


Quando escrevia o programa notei que estava fazendo errado, que o código iria gastar tempo demais inutilmente, então reescrevi o código resolvendo isto.

A primeira versão do programa foi:

#include        <stdio.h>

int     main()
{
        register unsigned long long numero, impar, soma ;

        for( numero = 1 ; numero <= 1000000000 ; numero++ )
        {
                register int    i ;

                for( i = numero, impar = 1, soma = 0 ; i ; i--, impar +=2 )
                        soma += impar ;

                if( soma != numero*numero )
                        printf( "Falhou para o numero %llu\n",numero );
        }
}


Esta é a descrição literal do problema, mas nem sempre a descrição literal é a melhor a ser implementada. O que está errado neste programa?

sexta-feira, 13 de novembro de 2015

C: Strings e ponteiros, um caso para pensar

O que está errado com função abaixo:

char mensagem[100] ;

processa()
{
    /* processa */
    if( /* uma condição de erro */ )
        strcpy( mensagem,"Deu erro 1" );
    else
    {
        /* processa */
        if( /* uma condição de erro */ )
            strcpy( mensagem,"Deu erro 2" );
        else
        {
            /* processa */
            if( /* uma condição de erro */ )
                strcpy( mensagem,"Deu erro 3" );
            else
                strcpy( mensagem,"Funcionou direitinho" );
        }
    }
}

Antes de prosseguir lendo, pare e pense um pouco.

C: Sobre strings e uma pequena eliminação de strlen, strcpy

Existem alguns truques que podem ser usados para acelerar processamento de strings, mas exigem uma compreensão de como funcionam as strings em C.

As strings em C são chamadas de Null Terminated Strings, ou strings terminadas por nulo. O caractere null, que é equivalente ao número inteiro zero (note, não é o caractere zero), estará logo após do último caractere da string, marcando que a string terminou.

E o conceito de string se confunde com o array de caracteres. Na realidade, é um array de caracteres, e o que vai ditar se é uma string ou um array de caracteres banal é a forma com que o programador fez o código lidar com ela.

quarta-feira, 16 de setembro de 2015

A fonte de dores de cabeça

Tive umas instabilidades no meu computador, especialmente ligadas a discos.

Eram discos que ficavam erráticos, dando erros, causando panes no sistema etc. Sorte que o FreeBSD, o sistema que uso, lida relativamente bem com estas coisas, dentro do possível. Na versão 10, se um disco dá problemas, sem ser um dos fundamentais do sistema, ele é abandonado, mas o sistema dificilmente dá pane. A versão 11, que estou usando, e que ainda está em desenvolvimento, me pareceu lidar pior com isto.

Eu aposentei alguns HDs por que deram algumas panes, mas paradoxalmente o S.M.A.R.T. tinha me dito que estavam bons. Troquei alguns adaptadores de alimentação, do tipo MOLEX  para SATA, e alguns cabos SATA, e tive poucas melhoras.

Um dos HDs aposentados era o antigo do meu notebook, que estava em um gabinete externo. Ele tinha parado de funcionar.

Recentemente a situação ficou pior, e cheguei a pensar que o HD novo de 4 TB estava morrendo, pois ele dava pane e era abandonado pelo sistema com muita frequência. A piora, em parte, coincidiu com a colocação de um SSD, além dos 5 HDs que costumavam ficar ligados.

Mas a coisa estava caótica demais para parecer que era ele. Fiz trocas de cabos SATA e não deu em nada.

Comecei a desconfiar de problemas de fonte de alimentação, mesmo com o sistema dando panes relacionadas com HDs, e não com placa mãe. A fonte poderia não estar aguentando tanta carga de HDs.

A BIOS tem um recurso que é mostrar as tensões da fonte de alimentação, então, depois de uma das muitas panes, entrei nela para ver. As tensões de 3.3V e 5.0V estavam muito baixas, com cerca de 3.0V e 4.5V respectivamente. Estavam com 10% de erro, que é mais do que os 5% permitidos no padrão esperado. A de 12V estava boa.

Como muita da eletrônica dos HDs dependem da alimentação de 5.0V, usando a de 12V só para os motores, pensei em panes das controladoras dos HDs. E o SSD não tem motor, portanto só deve usar a alimentação de 5.0V, talvez tenha aumentado mais ainda a carga sobre ela.

Num teste rápido, eu desliguei o gravador de DVD e um dos HDs, e as fontes de 3.3V e 5.0V melhoram um pouquinho, com a segunda marcando 4.6V. Mesmo assim o sistema ficou instável, só que menos instável.

Então resolvi arriscar comprar uma fonte nova, de 500W, como a antiga (que eu pensava se de 430W). Foi uma boa compra. Ela é mais silenciosa, e a medição de tensão ficou praticamente cravada, variando cerca de 1% por volta da valor nominal. Isto com 5 HDs internos, um USB e um SSD.

Fui colocando carga, fazendo os testes nos discos, colocando o gravador de DVD etc. O sistema se mostrou estável, bem estável.

No momento estou com 5 HDs internos, 2 USB (o antigo do notebook mencionado acima voltou a funcionar, portanto deveria ser problema com a alimentação de 5.0V), 1 SSD, o gravador de DVD plugados, e está tudo estável.

Fontes de alimentação podem ser fontes de grandes dores de cabeça, além de serem a peça mais chata para substituir num computador. Elas podem sutilmente lhe infernizar a vida.

PS: Agora tenho que retestar um bando de HDs aposentados. Sorte que não joguei-os no lixo.

domingo, 25 de março de 2012

C: Um exemplo de simplificação de expressão lógica

No outro dia vi uma expressão assim:

        if(((situacao == situacao_a) ||
            ((
situacao == situacao_b) && (strcmp(string,UMA_MACRO) == 0))) ||
            ((
situacao == situacao_c) ||
            ((
situacao == situacao_d) && (strcmp(string,UMA_MACRO) == 0))))
        {

                        /* ... */
        }


Este código acima está abaixo do ótimo, tanto em geração de código quanto em desempenho. Mas como otimizar?

Antes de continuar lendo, que tal você mesmo tentar fazer uma versão melhor dele? O artigo sobre avaliação incompleta pode ajudar.

sábado, 3 de março de 2012

C: Contagem de condições lógicas

Digamos que em um algoritmo é necessário que pelo menos 3 de 5 condições sejam verdadeiras para tomar uma ação. Como testar isto? Fazer um emaranhado de if? Pior, se somente 3 de 5 tem que ser verdadeiras? Fazer um emaranhado maior?

C: Avaliação incompleta

Existem duas formas de avaliação em expressões lógicas, a avaliação completa e a incompleta.

Por definição, no Pascal a avaliação sempre é completa (O Turbo Pascal tinha uma opção para mudar este comportamento.), e no C é incompleta.

O que é isto? Quais são as consequências? Como melhor aproveitar isto.

sexta-feira, 24 de fevereiro de 2012

C: Ser ou não ser - Boolean

No C não existe o tipo boolean, tal como existe em outras linguagens, mas existem operações lógicas. As operações lógicas são operações inteiras de baixo nível de precedência.

C: Explicando as bruxarias

A linguagem C permite uma economias de expressão, e outras linguagens podem ter seus truques também, então é aconselhável o uso de comentários explicando as "bruxarias" que você fez. Um exemplo pode ser visto abaixo:

Não há nada mais inseguro do que um procedimento problemático de segurança

Se você e/ou a empresa tem um procedimento de segurança que cause impedimentos inúteis, erros de conexão, muitas senhas etc, as pessoas ficam aborrecidas e tendem a não cumprir, e até contornar, os procedimentos de segurança.

Os procedimentos de segurança tem que fazer parte do dia a dia, mas não podem ser custosos e/ou penosos de serem seguidos, senão existirá uma tendência de serem abandonados.

Um exemplo é o mito da troca periódica de senha. Depois de um tempo a pessoa fica sem imaginação e começa a repetir senhas, trocar somente uma letra ou número da senha, usar senhas fáceis e óbvias, confundir senhas, esquecê-las etc. É isto que acontece com a mitológica segurança da troca frequente de senhas. Antes uma senha muito boa usada por muito tempo, em um ambiente seguro, do que trocas mensais de senha. Se a senha vazar, não importa se vai demorar 2 semanas para ser trocada, ou um ano, pois quem está afim de fazer um estrago, ou usá-la de forma errada, quase certamente fará no menor tempo possível, já prevendo que a senha logo será trocada.