Estou escrevendo um programa sobre geometria. Não quero entrar em detalhes ainda, o farei quando tiver mais o que mostrar.
Durante a codificação foi necessário obter as coordenadas da intersecção de duas circunferências. É um problema enganosamente simples, e sem querer pensar muito você pode encontrar várias soluções na Internet [SOF1] [SEX1] [WMW1]
Mas estas soluções, ou partem de raciocínios geométricos para os vários casos, ou não são gerais o suficiente (considerando um sistema de coordenadas que facilita a solução por exemplo, o que embora seja esperto não resolve o problema geral sem passos adicionais, em geral não descritos) ou as duas coisas [WMW1].
Eu resolvi fazer a minha solução e comecei como todos analisando os casos, aparentemente óbvios: quando as circunferências estão longe demais para se cruzar, quando se tocam em um ponto (isso é chamado de circunferências osculatórias), quando se tocam em dois pontos. Simples não? Mas ao especificar as condições para poder fazer o programa surgiram outros casos: quando as duas circunferências tem o mesmo centro mas raios diferentes, ou seja não se cruzam, quando o centro de uma circunferência está dentro da outra mas elas se cruzam em um ou dois pontos.
Comecei e pensar que isso estava virando uma confusão de if’s, mesmo antes que esta parte do programa estivesse pronto para compilar [1]. Será que não haveria outro caso, ou será que meus if’s tinham condições que eram mais genéricas e abarcavam outras condições específicas contidas nelas? Neste caso ele nunca passaria pelas específicas se as gerais estivessem antes no if. Como tudo em programação uma coisa banal, não é tão banal se olhada de perto.
The Art of UNIX programming
Resolvi utilizar uma coisa que aprendi há muito tempo: que frequentemente em programação a formalização de um problema em bases matemáticas sólidas o torna muito mais simples.
No livro The Art of UNIX Programming [RAY1] [2], Eric Raymond escreve:
Uma sutil, porem poderosa maneira de promover a compacidade em um projeto é organiza-lo em torno de um forte algoritmo central que ataca uma definição formal clara do problema, evitando heurística e evasivas.
“A formalização frequentemente esclarece uma tarefa espetacularmente. Não é suficiente para um programador reconhecer que partes de sua tarefa caem dentro de categorias padrão da ciência da computação – um pouco de busca primeiro-em-profundidade (DFS) aqui e um pouco de quicksort ali. Os melhores resultados ocorrem quando o ponto central do problema pode ser formalizado, e um modelo claro do trabalho em questão pode ser construído. Não é necessário que o usuários finais compreendam o modelo. A simples existência de um núcleo unificador proporcionará uma sensação de conforto, sem o fardo daqueles momentos porque-diabo-eles-fazem-assim que são tão frequentes quando se usam programas do tipo canivete suíço.”
-- Doug McIlroy
Este é um ponto forte da tradição Unix comumente esquecido. Muitas das suas ferramentas mais efetivas são cascas finas ao redor de uma tradução direta de um algoritmo simples e poderoso.
Talvez o mais claro exemplo disso seja o diff(1), a ferramenta Unix para exibir diferenças entre arquivos relacionados. Esta ferramenta e seu dual, patch(1), se tornaram centrais ao estilo de desenvolvimento distribuído em rede do Unix moderno. Uma propriedade valiosa do diff é que ele raramente surpreende qualquer um. Ele não tem casos especiais ou dolorosas condições extremas, porque ele usa um método de comparação de sequências simples e razoável matematicamente. Isso tem consequências:
“Em virtude de um modelo matemático e um algoritmo sólido, o diff do Unix se contrasta marcadamente de seus imitadores. Primeiramente, o maquinário central é sólido, pequeno e nunca precisou de uma linha de manutenção. Segundo, os resultados são claros e consistentes, sem ser desfigurados por surpresas onde a heurística falha.”
-- Doug McIlroy
Assim as pessoas que usam o diff desenvolvem, uma sensação intuitiva do que ele fará em uma dada situação sem necessariamente entenderem o algoritmo central perfeitamente. Outros exemplos bem conhecidos desse tipo especial de clareza conseguida por meio de um algoritmo central são abundantes em Unix:
O utilitário grep(1) para selecionar linhas de arquivos por reconhecimento de padrões é uma casca simples em torno de uma álgebra formal de padrões de expressões regulares (veja a seção chamada “Estudo de Caso: Expressões regulares” para uma discussão). Se ele carecesse desse modelo matemático consistente, ele provavelmente pareceria com o projeto original do aplicativo glob(1) dos antigos Unixes, um punhado de wildcards ad-hoc que não podiam ser combinados. O utilitário yacc(1) para gerar parsers de linguagens é uma casca fina ao redor da teoria formal das gramáticas LR(1). Seu parceiro, o analisador-gerador léxico lex(1) , é de modo similar uma casca fina ao redor da teoria de autômatos de estados finitos não determinísticos.Todos estes três programas são tão livres de bugs que seu funcionamento correto é dado completamente como certo e compacto o suficiente para caber na mão do programador. Apenas uma parte dessas boas qualidades são em virtude do polimento que vem com uma longa vida de serviço e uso frequente; a maioria vem do fato de que tendo sido construído ao redor de um núcleo algorítmico forte e provadamente correto, eles nunca precisaram de muito polimento em primeiro lugar.
Resolvi fazer exatamente isso e tratar o simples problema da intersecção de duas circunferências formal e exatamente em vez de pensar em cada caso especial.
A geometria (segundo Descartes)
Eu queria uma solução abrangente e que me desse garantia que eu tivesse todos os casos. Preferi uma abordagem de geometria cartesiana desde o início. Para isso é só escrever as equações de duas circunferências em posição arbitrária, mas se você fizer isso vai ficar perdido em um mar de letras ao tentar fazer as contas, isso porque você terá as duas coordenadas de cada centro e vai ficar carregando elas o tempo todo, sendo que a informação relevante é apenas o raio das circunferências e a distância dos centros, o resto é dependente da escolha de coordenadas.
Você pode utilizar um programa de computação algébrica [3]. Se você utilizar o Maxima e colocar a equação geral e mandar resolver não vai obter nada [4], apenas uma lista vazia. Por outro lado se for escolhido um sistema de coordenadas adequado simplifica-se muito o problema [5]. Basta escolher a origem como um dos centros e o eixo $x$ na reta que une os centros. Se o caso original não for esse sempre podemos converter um sistema de coordenadas no outro.
Com este sistema de coordenadas, temos as seguintes equações:
\[{x}^{2}+{y}^{2}={r}_{1}^{2}\]
\[{\left( x-d\right) }^{2}+{y}^{2}={r}_{2}^{2}\]
Este sistema o Maxima resolve facilmente [6] (embora demore uns minutos) encontrando as coordenadas dos pontos de intersecção:
\[x=\frac{{d}^{2}-{r}_{2}^{2}+{r}_{1}^{2}}{2\,d},y=-\frac{\sqrt{-{d}^{4}+2\,{r}_{2}^{2}\,{d}^{2}+2\,{r}_{1}^{2}\,{d}^{2}-{r}_{2}^{4}+2\,{r}_{1}^{2}\,{r}_{2}^{2}-{r}_{1}^{4}}}{2\,d}\]
\[x=\frac{{d}^{2}-{r}_{2}^{2}+{r}_{1}^{2}}{2\,d},y=\frac{\sqrt{-{d}^{4}+2\,{r}_{2}^{2}\,{d}^{2}+2\,{r}_{1}^{2}\,{d}^{2}-{r}_{2}^{4}+2\,{r}_{1}^{2}\,{r}_{2}^{2}-{r}_{1}^{4}}}{2\,d}\]
repare que a solução aparece apenas em termos dos raios de da distância entre os centros. nada de coordenadas dos centros das circunferências. Mas isso ainda parece complicado. Vamos chamar o termo sob o radical de determinante e fatorá-lo, assim obtemos:
\[det=-\left( d-{r}_{2}-{r}_{1}\right) \,\left( d-{r}_{2}+{r}_{1}\right) \,\left( d+{r}_{2}-{r}_{1}\right) \,\left( d+{r}_{2}+{r}_{1}\right) \]
Isso foi interessante pois diminui as multiplicações e somas [7], e porque isso dá as condições de uma maneira natural. Simplificando usando a notação ${s}_{r}={r}_{2}+{r}_{1}$ e ${d}_{r}={r}_{2}-{r}_{1}$, obtemos:
\[det=-\left( {d}_{r}-d\right) \,\left( {d}_{r}+d\right) \,\left( {s}_{r}-d\right) \,\left( {s}_{r}+d\right) \]
Expressando a solução assim, temos:
\[x=\frac{dr\,sr-{d}^{2}}{2\,d},y=-\frac{\sqrt{det}}{2\,d}\]
\[x=\frac{dr\,sr-{d}^{2}}{2\,d},y=\frac{\sqrt{det}}{2\,d}\]
Daqui tiramos as condições a partir do determinante, pois como ele está sob um radical ele tem de ser positivo ou não há solução. Como ele é um produto de quatro termos o sinal depende do sinal dos termos, como ${s}_{r}$ é sempre positivo (já que ${r}_{1}$ e ${r}_{2}$ são positivos sempre) e ${d}$ também é sempre positivo pois é uma distância.
Quanto a ${d}_{r}$ ele é ${r}_{1}-{r}_{2}$ mas isso parece arbitrário pois ${r}_{2}$ pode ser o raio maior ou o menor que ${r}_{1}$, é apenas uma questão de qual nome eu dou a cada raio. Assim ele poderia ser positivo ou negativo, mas ${d}_{r}$ aparece em apenas dois termos $\left( {d}_{r}-d\right) \,\left( {d}_{r}+d\right) $ e expandindo temos ${d}_{r}^{2}-{d}^{2}$, ou seja, o sinal de ${d}_{r}$ não altera o resultado (por aparecer nos dois termos e ficar ao quadrado na expansão), assim podemos tomar ${d}_{r}$ em módulo, simplificando nossas considerações.
Assim o sinal de ${det}$ depende dos termos que são subtrações que podem ser positivos ou negativos. $\left( {d}_{r}-d\right) $ e $\left( {s}_{r}-d\right)$. Temos 4 casos, então:
${s}_{r}>d$ | ${s}_{r}<d$ | |
${d}_{r}>d$ | $det<0$ | $det>0$ |
${d}_{r}>d$ | $det>0$ | $det<0$ |
Fica bem mais simples de especificar os casos com a certeza que eles são abrangentes. Vamos ver o significado de cada caso:
- ${s}_{r}<d,{d}_{r}>d$: este caso tem solução, mas dadas as limitações dos valores de ${r}_{1}$, ${r}_{2}$ e ${d}$ ele nunca acontece, se temos ${r}_{2}+{r}_{1}<d$ e ${r}_{2}-{r}_{1}>d$ teríamos ${r}_{2}+{r}_{1}<{d}<{r}_{2}-{r}_{1}$, ou ${r}_{2}+{r}_{1}<{r}_{2}-{r}_{1}$ o que é impossível para ${r}_{1}$ e ${r}_{2}$ positivos;
- ${s}_{r}>d,{d}_{r}>d$: este caso é possível, e não tem solução, pois uma circunferência está dentro da outra e a menor não toca a maior porque é pequena demais para elas se cruzarem;
- ${s}_{r}<d,{d}_{r}<d$: este caso é possível, e não tem solução, pois as circunferências tem seus centros afastados demais para que elas se toquem;
- ${s}_{r}>d,{d}_{r}<d$: este caso é possível, e tem solução, as circunferências se tocam em dois pontos.
Ainda temos mais um caso a analisar:
- $det=0$: temos apenas uma solução as circunferências osculam, se tocando em apenas um ponto. Para isso basta que um dos termos seja 0 (já que estão multiplicando), ou seja ${s}_{r}=d$ ou ${d}_{r}=d$ já os termos em soma não podem ser 0. Se ${s}_{r}=d$ temos as circunferências osculando por fora,e se ${d}_{r}=d$ temos as circunferências osculando por dentro;
Além dos valores de $det$ temos ainda dois casos para analisar:
- $d=0, {r}_{1}\not={r}_{2}$: como $d$ está no divisor da solução, temos uma divisão por 0 então não há solução. Isso representa circunferências concêntricas uma com raio menor que a outra;
- $d=0, {r}_{1}={r}_{2}$: agora na expressão da solução além do 0 no denominador temos 0 no numerador. Vamos entender o que isso significa, a equação das duas circunferências é a mesma, ou seja temos uma equação só e a solução para a interseção é o conjunto de todos os pontos que satisfazem a equação, ou seja a circunferência toda. Não é que ela não tenha solução, ela tem infinitas soluções. Neste caso em matemática se diz que o sistema é compatível, mas indeterminado. Isso representa duas circunferências concêntricas de mesmo raio.
Mas ainda não acabou, temos de reconverter as coordenadas para o sistema antes de simplificar o sistema de coordenadas. Isso vai nos dar a resposta geral.
Podemos considerar a transformação entre dois sistemas de coordenadas ortonormais como uma translação seguida de um giro. A translação desloca a posição da origem e a rotação gira os eixos até se surperporem. A translação é apenas uma soma (ou subtração), a rotação é um pouco mais complexa e em geral é representada por uma matriz de rotação aplicada as coordenadas [8].
\[\begin{bmatrix}x\prime\cr y\prime\end{bmatrix}=\begin{bmatrix}\mathrm{cos}\left( \theta\right) & \mathrm{sin}\left( \theta\right) \cr -\mathrm{sin}\left( \theta\right) & \mathrm{cos}\left( \theta\right) \end{bmatrix} . \left( \begin{bmatrix}x\cr y\end{bmatrix}-\begin{bmatrix}{x}_{0}\cr {y}_{0}\end{bmatrix}\right) \]
Onde $(x\prime, y\prime)$ são as coordenadas no sistema de coordenadas simplificado e $(x,y)$ são as coordenadas no sistema original mais geral. Como temos o resultado em termos de $(x\prime, y\prime)$ precisamos inverter esta expressão [9]. Isso pode ser feito de várias maneiras, o que nos dá o seguinte resultado:
\[\begin{bmatrix}\mathrm{cos}\left( \theta\right) & -\mathrm{sin}\left( \theta\right) \cr \mathrm{sin}\left( \theta\right) & \mathrm{cos}\left( \theta\right) \end{bmatrix} . \begin{bmatrix}x\prime\cr y\prime\end{bmatrix}+\begin{bmatrix}{x}_{0}\cr {y}_{0}\end{bmatrix}=\begin{bmatrix}x\cr y\end{bmatrix}\]
Um estilo de vida
Agora temos a solução generalizada. Mas não é apenas isso, com a análise que fizemos o problema ficou resolvido de maneira elegante, sabemos que a solução é abrangente e ela nos sugeriu uma maneira eficiente e expressiva de implementá-la.
O que quero passar aqui não é tanto a solução do problema da intersecção de duas circunferências, mas a maneira de abordar o problema, que é criar um modelo formal e sólido do problema e não criar uma colcha de retalhos de casos particulares ou extremos.
Mas é incrivelmente complicado convencer as outras pessoas disso. Com o tempo percebi que a relutância das pessoas em aceitar processos mais formais não era fruto de um ceticismo saudável, mas era motivada pelo desconhecimento dessas estruturas ou algoritmos. Elas os viam como abstrações teóricas sem contato com a realidade e preferiam uma abordagem concreta, através de uma heurística tropeçante.
Eu tenho uma crença, bem diferente das aceitas pelos modismos atuais. A de que o software deve ser construído como uma catedral, visando algo maior. Alguns dirão que minha analogia é falha as catedrais não eram feitas por dinheiro, a estes eu lembro que muitos softwares fundamentais hoje também não.
A maioria me achará um louco dizendo que tempo é dinheiro, que tem de haver resultados rápidos para que se possa ganhar dinheiro. E eu digo que um pouco menos de velocidade e um pouco mais de consideração evita que se perca dinheiro. Evitando um círculo vicioso de correções de erros ou inadequações que precisam ser feitas mais rapidamente ainda que o produto original para corrigir seus problemas, e essa urgência provoca novos problemas em ciclos cada vez mais rápidos e cada vez menos efetivos, onde as correções introduzem problemas que com frequência são maiores ainda. E tudo isso poderia ser evitado. Se você se incomoda profundamente com sistemas de atualização invasivos como os dos produtos da Adobe e da Microsoft, talvez entenda onde quero chegar.
O que eu proponho? É simples, se o software não dá prazer em ser feito, se ele não parece elegante, belo e robusto ao final então eu não quero ter nada a ver com ele. Isso funciona para mim, e me manteve empregado e produtivo por décadas. Se você pensa que para que possa fazer da programação uma carreira não pode pensar assim, está deixando de fazer algo que poderia ser muito agradável. Basicamente eu proponho isso como um estilo de vida.
A arte da fuga
Há décadas atrás eu vi uma entrevista com Juca Chaves onde ele se referia a algo que era técnico dizendo que aquilo não é arte. Acredito que uma peça de tecnologia ou ciência é arte sim, um projeto de um processador, um FPGA, a prova de um teorema matemático ou programa de computador pode ser arte sim.
Mas existem algumas diferenças, uma delas é que estas coisas não tem a o objetivo original de serem apreciadas como arte. Mas uma catedral também não tem esse objetivo principal, ela é construída com um objetivo prático que não é o da arte, mas isso não impede que ela seja feita como uma obra de arte para atingir esse objetivo. Nesse ponto em particular um programa de computador também pode ser feito assim.
Assim como um artista se utiliza da arte como maneira de se expressar, um programador também pode usar um programa como maneira de se expressar, quando ele faz isso temos arte de verdade, intencionalmente.
Outra diferença é que, uma peça de tecnologia ou ciência é uma forma de arte que não pode ser devidamente apreciada com facilidade, ela exige esforço e conhecimento.
Me permita traçar um paralelo: Bach no fim da vida era visto por muitos, como seus filhos, como Carl Philipp Emanuel como uma relíquia, sua música polifônica cheia de canones e fugas era considerada fora de moda. Achava-se que era sofisticada demais para que o público pudesse apreciá-la. O seu empregador, o pastor onde ele era kapellmeister pensava o mesmo, que a sofisticação de sua música em vez de louvar a Deus, distraía os fiéis dele.
Carl Philipp Emanuel o chamou à corte de Frederico, o Grande, da Prússia onde ele foi tratado com um misto de reverência e escárnio, como se quisessem provar que sua grandeza embora reconhecida era obsoleta [GAI1].
No entanto hoje quando se fala em Bach pensamos primeiro em Johann Sebastian e não em Carl Philipp Emanuel ou Johann Christian, a ponto que eu possa me referir a ele como Bach sem medo de ser ambíguo.
Era verdade que música de Bach era sofisticada, era verdade também que o público não a apreciava em sua totalidade, mas isso era um problema do público e não dele. Eu quando era adolescente gostava de Bach, mas até gostava das músicas de Carl Phillip Emmanuel um pouco mais, então um dia li um livro: Gödel, Escher, Bach: an Eternal Golden Braid [HOF1], onde me foram ensinados entre outras coisas os detalhes da polifonia e as múltiplas vozes se separavam e se uniam novamente quando eu escutava uma fuga e Bach nunca mais soou o mesmo de novo.
Com o tempo este tipo de experiência se repetiu vezes sem conta e percebi que cada vez que eu aprendia sobre algo que não gostava, ou não gostava tanto de início, isso me fazia ter uma nova visão que me levava a apreciar aquilo.
Cheguei a conclusão que o problema de algo que eu não gostava em geral estava na minha cegueira e ignorância e não na coisa que eu não apreciava, foi assim que passei a apreciar ópera por exemplo, mas esta era uma sensação que não era diferente das sensações que eu tive quando era criança quando consegui aprender a falar números de mais de dois dígitos, ou quando consegui projetar um circuito com um relê que mantinha memorizava seu estado, num análogo mecânico de um flip-flop, ou quando entendi os números binários ou quando eu entendi porque a integral que dava os coeficientes de um série de Fourier funcionava, ou quando descobri que a expansão em série de Taylor da exponencial levava a uma soma de seno e cosseno se fosse aplicada a um ângulo imaginário.
Estes são momentos caros da minha vida, tão caros como o cheiro do churros do zoológico ou a lembrança da minha avó. São momentos que mostram para mim que não existe vergonha na ignorância, existe vergonha apenas em achar que a ignorância sobre algo é de alguma maneira uma virtude, e deixar passar a oportunidade de aprender. É uma maravilha que exista sempre algo a aprender, ou a vida seria triste.
LeoNerd
A sensação de encantamento com estas coisas que eu descobri em nada diferia da sensação de encantamento com os quadros de Leonardo (eu gostava em particular das duas versões da Virgem dos Rochedos), e é interessante notar que ele não diferenciava em importância sua arte e sua técnica não só em pintura ou escultura, mas no projeto de dispositivos como o orntóptero e o paraquedas. Quando Bill Gates gastou uma fortuna comprando alguns manuscritos de Leonardo [WIKI1] eu entendo bem porque, podemos dizer que ele foi um nerd e para ele tudo isso era arte, ao contrário do que Juca Chaves acha.
Notas
[1]. Isso é um code smell chamado complex conditionals. Isso é sempre uma indicação de uma abordagem heurística e de futuros problemas.
[2]. A versão eletrônica deste livro usa a licença CreativeCommons, com a reserva dos direitos da versão impressa para a editora. Assim é fácil encontrar versões online dele, como as em [RAY1] e [RAY2].
[3]. Isso em geral é chamado de CAS (computer algebra system). Existem vários como: Maxima (que é uma versão GPL do Macsyma desenvolvido no MIT), Maple, Mathematica, Reduce (que já foi proprietário mas hoje é aberto com licença BSD), algumas calculadoras como a TI Nspire CAS (que utiliza uma versão do antigo Derive que era um programa leve o suficiente para poder ser implementado no processador e uma calculadora), e a HP50g.
[4]. No Maxima faríamos assim para resolver:
circle1:(x-x[1])^2+(y-y[1])^2=r[1]^2;
circle2:(x-x[2])^2+(y-y[2])^2=r[2]^2;
solve([circle1,circle2],[x,y]);
[5]. Sim, eu sei, foi exatamente o que critiquei antes, mas eu vou mostrar os passos para estender ao caso geral depois.
[6]. Para resolver o sistema simplificado no Maxima:
normalized_circle1:x^2+y^2=r[1]^2;
normalized_circle2:(x-d)^2+y^2=r[2]^2;
solve([normalized_circle1,normalized_circle2],[x,y]);
[7]. Isso é parecido com a avaliação de polinômios de Horner, onde um polinômio de grau $n$ é calculado com apenas $n$ somas e $n$ multiplicações.
[8]. Para entender porque uma matriz é preciso conhecer um pouco de álgebra linear. Basicamente aplicamos uma transformação a cada ponto onde cada coordenada no sistema novo depende de suas duas coordenadas originais multiplicadas por fatores uma para o x outro para o y. A coordenada é transformada como a equação de uma reta, onde andando na direção de um dos eixos de coordenadas original eu me movo um pouco em cada eixo do sistema de coordenadas novo tanto menos quanto cada um deles está inclinado em relação ao eixo da direção onde me movo. Estes fatores de proporcionalidade são os senos e cossenos da inclinação entre os eixos. Existe uma infinidade de maneiras de ver e entender isso, por projeção, por produto escalar, equação de retas, etc. Você tem muito a ganhar em insight explorando estas maneiras, mas foge do meu objetivo aqui explicar mais detalhadamente isso.
[9]. Pode-se multiplicar os dois lados pela inversa da matriz de rotação. É trivial inverter uma matriz 2x2 (sim álgebra linear de novo, como dizia meu professor do cursinho, “1 sobre o determinante vezes a matriz dos cofatores transposta”). Mas temos a ganhar em insight e admiração da beleza da consistência explorando outros caminhos. Inverter a expressão também significa uma rotação para o lado oposto. Como seno é uma função ímpar (ou seja $sin(\theta)=-sin(-\theta)$ e cosseno é uma função par (ou seja $cos(\theta)=cos(-\theta)$) simplesmente ao substituir $\theta$ por $=-\theta$ na matriz de rotação temos:
\[\begin{bmatrix}\mathrm{cos}\left( -\theta\right) & \mathrm{sin}\left( -\theta\right) \cr -\mathrm{sin}\left( -\theta\right) & \mathrm{cos}\left( -\theta\right) \end{bmatrix}=\begin{bmatrix}\mathrm{cos}\left( \theta\right) & -\mathrm{sin}\left( \theta\right) \cr \mathrm{sin}\left( \theta\right) & \mathrm{cos}\left( \theta\right) \end{bmatrix}\]
Que também é a inversa. Repare que a inversa é igual a transposta (em matemática uma matriz com esta característica é chamada de ortogonal).
Referências
[SOF1]: python - Circle-circle intersection points - Stack Overflow. Disponível em: <http://stackoverflow.com/questions/3349125/circle-circle-intersection-points>. Accesso em: 21 mai. 2014.
[SEX1]: intersection - How can I find the points at which two circles intersect? - Mathematics Stack Exchange. Disponível em: <http://math.stackexchange.com/questions/256100/how-can-i-find-the-points-at-which-two-circles-intersect>. Accesso em: 21 mai. 2014.
[WMW1]: Circle-Circle Intersection -- from Wolfram MathWorld. Disponível em: <http://mathworld.wolfram.com/Circle-CircleIntersection.html>. Accesso em: 21 mai. 2014.
[RAY1]: RAYMOND, Eric S. The Art of UNIX Programming. Boston: Addison-Wesley, 2003. p. 111, cap. 4.
[RAY2]: The Art of UNIX Programming: Compactness and Orthogonality: Compactness and the Strong Single Center. Disponível em: <http://www.faqs.org/docs/artu/ch04s02.html#id2894555>. Accesso em: 26 mai. 2014.
[RAY3]: The Art of Unix Programming : Eric Steven Raymond : Free Download & Streaming : Internet Archive. Disponível em: <https://archive.org/details/ost-computer-science-the_art_of_unix_programming-1>. Accesso em: 26 mai. 2014.
[GAI1]: GAINES, James R. Uma noite no palácio da razão. Rio de Janeiro: Record, 2007.
[HOF1]: HOFSTADTER, Douglas R. Gödel, Escher, Bach: an eternal Golden Braid. London: Penguin Books, 1980.
[WIKI1]: Codex Leicester. In: Wikipedia. Disponível em: <http://en.wikipedia.org/wiki/Codex_Leicester>. Acesso em: 02 jun. 2014.
Recent Comments