SysInternals

Hace algunos años Mark Russinovich tenía un sitio llamado SysInternals donde no solo ofrecía rutinas para realizar operaciones de administración de Windows sino también el correspondiente código fuente y artículos con explicación detallada de la lógica de programación. Afortunadamente para él y desafortunadamente para los hackers curiosos Microsoft le compro el sitio y quito el acceso a los códigos fuentes y las explicaciones sobre el funcionamiento interno de Windows.

 
Ahora Microsoft incluye Windows SysInternals como parte de su sito de soporte con un enfoque de administración de sistemas en vez del de programación que originalmente tenia. No queda más que comprar Windows® Internals: Including Windows Server 2008 and Windows Vista, Fifth Edition (PRO-Developer) y compensar a Russinovich por compartir sus experiencias.

La búsqueda de número primos

La búsqueda de número primos (GIMPS Home Page).

Inclusive hay recompensa económica para motivar el desarrollo de esta tecnología a través de EFF Cooperative Computing Awards para el que encuentre primero:

$50,000 por el primer número primo con más de 1,000,000 dígitos decimales ( Apr. 6, 2000)
$100,000 por el primer número primo con más de 10,000,000 dígitos decimales
$150,000 por el primer número primo con más de 100,000,000 dígitos decimales
$250,000 por el primer número primo con más de 1,000,000,000 dígitos decimales

Actualmente el primo más grande que se conoce es el primo Mersenne 44, 232,582,657-1, un número de 9,808,358 digitos así que el premio de los 100,000 dolares pudiera estar cerca.

¿Se podrá hacer algo mejor con tu PC que correr screen savers y tenerla esperando a que teclees la siguiente letra?


Levantamiento de requerimientos

El siguiente escenario es tí­pico: Un consultor trabaja con los usuarios para describir los procesos de negocio que serán soportados por el software. El equipo de desarrollo recibe la descripción del consultor pero no están familiarizados con los términos de negocio y consideran la descripción demasiado informal. Los desarrolladores escriben su propia descripción desde un punto de vista técnico. El usuario no entiende esta descripción pero la acepta para que el proyecto avance. El resultado puede ser un sistema que desde el punto de vista del usuario es difícil de usar y que no cumple con sus expectativas.

Parte de este problema es metodológico, y en parte es intrínseco a las caracterí­sticas de los usuarios. Algunas de las problemáticas que se presentan:

  • Los usuarios no saben que es lo que quieren
  • Los usuarios no aceptan como un compromiso los requerimientos escritos
  • Los usuarios insistirán en nuevos requerimientos después de fijar costos y agendas.
  • Los usuarios no están disponibles y la comunicación con ellos es lenta
  • Los usuarios no participan en revisiones de avance.
  • Los usuarios no entienden el proceso de desarrollo y no les interesa.

Existen herramientas y metodologías para el levantamiento de requerimientos. Casos de uso y UML son medios para formalizar este proceso. Que diagramas UML es apropiado usar dependerá del sistema a desarrollar.

Una guía simple en términos de la complejidad del sistema:

  • Aplicación mono usuario
    • Diagrama de casos de uso.
    • Diagrama de clases.
    • Diagrama de interacción.
  • Aplicación mono usuario, con manejo de eventos:
    • Añadir: Diagrama de estados.
  • Aplicación cliente servidor:
    • Añadir: Diagrama de despliegue y diagrama de componentes, dependiendo de la complejidad.
  • Aplicación compleja distribuida:
    • Todos.

Para una aplicación sencilla debemos realizar entre tres y seis tipos de diagramas, y para una aplicación compleja unos nueve tipos. El diagrama de casos de uso puede modelar el contexto de un sistema o los requisitos del mismo. Se puede extender la colección de elementos base de UML utilizando estereotipos.

Referencias:

La librería Gnu Multiple Precision (GMP)

La librería Gnu Multiple Precision (GMP) permite hacer cálculos de precisión arbitraria.

En el sitio de GMP viene un código de referencia que permite calcular pi hasta donde le alcance la memoria a la maquina.

Para construir la librería bajo mingw y Windows XP solo hay que seguir las instrucciones. Único detalle a tomar en cuenta es que /usr en mingw esta mapeado al directorio raíz de msys.

Posicionarse en el directorio raíz de la librería y seguir al secuencia del make

./configure
make
make check
make install

Ver el make trabajar es espeluznante, más de 10 minutos de pantallas can parámetros. Sin embargo hay puertos disponibles para .Net, aunque de la versión 4.1.

Para usar la librería podemos tomar como ejemplo el programa para calcular pi.

gcc -c gmp-chudnovsky.c -I/local/include
gcc -o gmp-chudnovsky.exe gmp-chudnovsky.o -L/local/lib -lgmp
gmp-chudnovsky.exe 50 1

Existe un puerto actualizado para Visual Studio 2005 disponible en la pagina Building GMP and MPFR with Microsoft Visual Studio 2005 and YASM. Hay que seguir las instrucciones del ReadMe con cuidado y al final aunque se generan warnings se construyen bien las librerías. Un paso que no esta claro del readme es que hacer con el archivo mparam_h.in. Yo simplemente lo renombre mparam.h.

El archivo gmp-chudnovsky.c del sitio de gmp necesita modificarse para usarlo en Visual Studio. Es necesario agregar las lineas de código:

#ifdef _MSC_VER
#define inline __inline
#endif

En la configuración del proyecto hay que agregar el directorio donde esta gmp.h y donde esta la librería que se quiera usar ademas de agregar la referencia a gmp.lib

Referencias

“Many Digits” Friendly Competition , Programas usados por el equipo de MPFR.

The MPFR Library

GMPY Project goals and strategies

Advanced Computation Group

Multiprecision floating-point arithmetic on Apple systems

Guile Extensions and Examples – Summary

AlgLibNet

Genius Math Tool and the GEL Language

Giac/Xcas

Computer algebra system

iRRAM – Exact Arithmetic in C++

MAGMA Computational Algebra System

SAGE is Open Source Mathematics Software

Wcalc

Numbers, constants, and computation

Minimalist GNU for Windows

MinGW o MinGW32 (Minimalist GNU for Windows) es una implementación de los compiladores GCC para la plataforma Win32, que permite migrar aplicaciones GNU a entornos Windows. Es un derivado de Cygwin en su versión 1.3.3.

MinGW incluye un conjunto de la api de Win32, permitiendo un desarrollo de aplicaciones nativas para esa plataforma, pudiendo generar ejecutables y librerí­as usando la API de Windows.

MinGW fue creado por Colin Peters, el 1 de julio de 1998, compilándolo con Gygwin. La primera versión nativa de MinGW fue realizada por Jan-Jaap van der Heijden, quien también tuvo participación en el proyecto GCC. Mumit Khan estuvo a cargo del mantenimiento del proyecto e incluyo al compilador algunas características propias de Windows. Los archivos de cabecera del API de Windows fueron provistos por Anders Norlander.

Una de las desventajas de MinGW es que los ejecutables que genera son de tamaño más grande que los generados por otros compiladores. Esto ocurre cuando se incluyen los archivos de cabecera estándares de C++ (por ejemplo, #include ), y se debe a que el compilador vincula todas las librerí­as dentro del archivo ejecutable de manera estática.

MinGW incluye MSYS (Minimal SYStem) un shell POSIX/Bourne para ejecutar scripts de configuración usados por make y ./configure

Después de descargar MinGW y MSYS, incluyendo mingw-runtime, w32api, binutils y gcc, gdb y mingw32-make se pueden expandir los archivos de dos formas. Poner el directorio de MinGW dentro de MSYS o instalarlos en directorios distintos y modificar el archivo MSYS /etc/fstab para agregar un apuntador al directorio donde mingw esta instalado.

Para probar la instalación se puede correr el shell de msys y probar el comando de línea

gcc –v

Para habilitar el soporte de IDEs agregar lib a la variable de entorno LIBRARY_PATH y los subdirectorios bin de y a la variable de entorno PATH
Aplicación de consola:

En un archivo con el nombre hello.c poner el siguiente código:

#include

int main(int argc, char **argv)
{
printf (“Hellon”);
return (0);
}

y compilar con

gcc -c hello.c

y después

gcc -o hello hello.o

Alternativamente

gcc -o hello hello.c

En un archivo con el nombre hello.cpp poner el siguiente código:

#include
int main(int argc, char **argv)
{
std::cout return (0);
}

y compilar con

g++ -c hello.cpp
g++ -o hello hello.o
Aplicación Windows

En un archivo con el nombre hello.c poner el siguiente código:

#include

int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow)
{
MessageBox (NULL, “Hello”, “Hello Demo”, MB_OK);
return (0);
}

para crear el ejecutable usar los comandos de linea

gcc -c hello.c

y

gcc -o hello hello.o -mwindows

el parametro -mwindows es necesario para que se incluyan las librerias necesarias para un programa Windows.
dll

En un archivo con el nombre dllfct.h poner el siguiente código:

#ifdef BUILD_DLL
// the dll exports
#define EXPORT __declspec(dllexport)
#else
// the exe imports
#define EXPORT __declspec(dllimport)
#endif

// function to be imported/exported
EXPORT void tstfunc (void);

En un archivo con el nombre dllfct.c poner el siguiente código:

#include
#include “dllfct.h”

EXPORT void tstfunc (void)
{
printf (“Hellon”);
}

En un archivo con el nombre Hello.c poner el siguiente código:

#include “dllfct.h”

int main ()
{
tstfunc ();
return (0);
}

Para crear una dll y un ejecutable que lo use:

gcc -c hello.c
gcc -c -DBUILD_DLL dllfct.c
gcc -shared -o tst.dll -Wl,–out-implib,libtstdll.a dllfct.o
gcc -o hello.exe hello.o -L./ -ltstdll

Se puede especificar el directorio a usar para los includes durante la compilación con

-I/path/to/headers

y las librerias para link:

-L/usr/lib/library

Usualmente no hay necesidad de andar moviendo las librerias.
Archivo .def para un dll

Si tiene un dll llamado file.dll y quiere crear un archivo .def con el nombre file.def,

echo EXPORTS > file.def
nm file.dll | grep ‘ T _’ | sed ‘s/.* T _//’ >> file.def

Para crear una biblioteca con el nombre file.a :

dlltool –def file.def –dllname file.dll –output-lib file.a

Generación de código

sisyphus

Así­ como los objetos fí­sicos se mueven a través del tiempo y el espacio, las aplicaciones de software se mueven por diferentes ejes en su ciclo de vida. Concretamente, el mantenimiento de una aplicación requerirá de una serie de adecuaciones y cambios por distintos motivos: cambio de plataforma, cambios en el proceso de negocio, nuevos requerimientos, nueva base de datos. Etcétera. Además, desde el punto de vista del desarrollador, lo ideal es poder reutilizar lo más posible de esfuerzos anteriores. En el ciclo de vida de un sistema, más del 60% del costo es el mantenimiento

La tendencia a requerimientos cada vez más complejos con tiempos de desarrollo cada vez más cortos vuelve el re-uso de código un imperativo.

Teóricamente, la programación orientada a objetos facilita el re uso de código. Los atributos de los lenguajes orientados a objetos que promueven el re-uso de código son:

Abstracción de datos que promueve sistemas modulares.

Herencia que permite que las subclases re-usen código de las superclases.

Poliformismo facilita el re-uso de comportamiento bajo diferentes contextos.

Marcos (frameworks) que son conjuntos de clases abstractas que solucionan familias de problemas relacionados.

Pero, para que se cumpla esta promesa divina de re-uso, el código, o más bien dicho, el diseño, debe ser bueno, muy bueno. ¿Qué es, entonces, un buen diseño? Cada cuál tendrá su idea. A manera de contrapunto empecemos por lo negativo, ¿Qué características tiene un mal diseño? Concediendo que la aplicación cumple con los requerimientos para los que fue diseñada, un diseño puede adolecer de lo siguiente:

Rigidez. La aplicación es difí­cil de cambiar porque cualquier cambio afecta demasiadas cosas.

Fragilidad. Cuando se hace un cambio, la aplicación deja de funcionar en lugares inesperados.

Inmovilidad. Es difí­cil utilizar la aplicación como parte de otra aplicación porque las dependencias con el contexto están enmadejadas en el código.

¿Qué es lo que hace un diseño rí­gido, frágil, e inmóvil? Las interdependencias de sus módulos. Entonces, para que el proceso de mantenimiento no sea la imagen de un perro persiguiendo su cola, se debe tener cuidado de minimizar interdependencias.

Desde el punto de vista de la programación estructurada, un diseño se puede ir construyendo bottom-up o top-down. Es decir, a partir de los componentes o módulos que tengo disponibles voy construyendo módulos más complejos, hasta cubrir los requerimientos, o conversamente, voy dividiendo mis requerimientos entre sub-módulos, hasta que llego a un punto suficientemente concreto para resolverlo de manera independiente. Al final, de manera iterativa, se llega a un diseño de capas jerárquico, donde en un sentido estricto, los únicos componentes que pueden ser completamente independientes del resto son los módulos de más bajo nivel en la jerárquia, es decir los más concretos y especí­ficos, los más ligados al problema particular que se esta resolviendo.

La mecánica establecida para re-usar rutinas de bajo nivel es el uso de librerí­as. En la practica la productividad de un lenguaje o ambiente de desarrollo esta ligada con la calidad y disponibilidad de librerías. Esto esta bien, pero desde el punto de vista de re-uso de código las dependencias están al revés. Los módulos de alto nivel, que resuelven un proceso complejo de interacción entre la aplicación, sus módulos internos, y el contexto exterior son los que queremos re-usar. De acuerdo a este precepto, un buen diseño debe cumplir con el principio de inversión de dependencias.

El principio de inversión de dependencias

Módulos de alto nivel no deben depender en módulos de bajo nivel. Ambos deben depender de abstracciones.

Abstracciones no deben depender de los detalles. Los detalles deben depender de las abstracciones.

Al concepto de inversión de control se le refiere como el principio de Hollywood:

Do not call us, we call you

Inversión de control es un aspecto clave que diferencia un marco orientado a objetos de una librería. Una librerí­a es un conjunto de funciones, tal vez organizadas dentro de clases, que una aplicación (cliente) llama dentro del código, la función hace lo que tiene que hacer, y regresa el control al cliente. En un marco orientado a objetos, es el marco el que llama al código del cliente. En .Net, por ejemplo, una manera de hacer esto es que el marco defina eventos a los cuales se subscribe el cliente y mediante el uso de delegados, el cliente asigna el compartimiento especifico que requiere.

En términos más generales, Interfaces es la manera de abstraer la interacción entre el cliente y el marco (framework). Una técnica de inversión de control es dependency injection.

La inversión de control es parte de lo que hace la programación de marcos orientados a objetos perturbadora para algunos. Al programar un procedimiento, la atención del programador esta en el flujo de control. Es difícil imaginar como se pudiera entender un programa sin saber la lógica de ejecución. Pero un buen marco abstrae el detalle del control de flujo. El foco de atención esta en los objetos, lo que puede ser al mismo tiempo más y menos tangible que el flujo de control.

En el marco, lo importante son las responsabilidades de cada objeto y la interacción (colaboración) entre ellos. Es una visión más abstracta, más declarativa del mundo, potencialmente más flexible y amplia que el enfoque en procedimientos.

Resumiendo, un buen diseño es aquel que nos permite hace cambios de manera no intrusiva, es decir, añadiendo código en vez de cambiando código. Un buen diseño se puede modificar sin tocar el código existente. O sea, en un desarrollo orientado a objetos el esfuerzo debe estar en el diseño. La programación orientada a objetos debe ser fácil, a costa de un proceso exhaustivo de diseño. He ahí la promesa y el reto fundamental de la orientación a objetos.

Los buenos patrones de diseño no se inventan, se descubren. Desde una perspectiva macro de diseño el principio de inversión de control es fundamental. Desde la perspectiva de las clases en si, ¿Qué principios se deben seguir para facilitar un buen diseño?, o más importante, ¿Qué debemos evitar para no inhibir el potencial del diseño? ¿Cómo garantizar que no haremos daño?

Algunos tips:

Es más fácil reusar un comportamiento agregando un componente que a través de herencia.

Eliminar análisis de casos. En el caso de .Net se pueden usar genéricos.

Reducir el número de argumentos. Sin embargo, al crear un objeto, es preferible exponer todas las dependencias para facilitar pruebas de clases individuales.

Reducir el tamaño de los métodos. El propósito del método debe ser obvio e idealmente el código debe ser auto documentado.

Las jerarquías de clases deben ser profundas y espigadas. Una clase todopoderosa, rodeada de ratoncitos indica un área de oportunidad en el diseño. Cada clase debe tener una responsabilidad principal, de preferencia única, claramente definida.

Minimizar acceso a variables. Una clase debe exponer solo aquello que sea estrictamente necesario para cumplir con su responsabilidad. En .Net se promueve el uso de propiedades en vez de variables públicas, para que la clase tenga mayor control en el uso de sus valores a través de métodos get y set.

La raíz de la jerarquía de clases debe ser abstracta. Es decir, no debe tener ningún (minimizar) detalle de implementación y solo exponer la interfaz de la clase.

Subclases deben ser especializaciones. Usualmente una subclase no debe redefinir métodos de la superclase, solo añadir nuevos.

Dividir clases grandes. Factorizar diferencias de implementación en sub-componentes. Separar métodos que no se comunican entre si.

Evitar el uso implícito de parámetros.

La guí­a del buen objeto,

  • Mantengo un estado consistente todo el tiempo.
  • No tengo métodos o variables estáticos.
  • Nunca espero o regreso null.
  • Fallo pronto.
  • Soy fácil de probar, todos los objetos de los que dependo los recibo como parámetros, normalmente durante construcción.
  • Los objetos de los que dependo pueden ser substituidos por Mock Objects (no uso dependencias a clases concretas).
  • Encadeno constructores multiples a un lugar comun, usando this
  • Siempre defino hashCode() junto a equals().
  • Prefiero valores inmutables que puede tirar fácilmente.
  • Tengo un valor especial para nada, por ejemplo EMPTY_SET para colecciones.
  • Disparo una excepción cuando el cliente pide algo que no es razonable, por ejemplo, abrir un archivo que no existe.
  • Disparo una excepción cuando no puedo hacer algo que deberí­a poder hacer, por ejemplo error de disco al leer un archivo abierto.
  • Solo atrapo excepciones que puedo manejar completamente.
  • Solo registró en bitácora información que alguien necesita ver.

Referencias

Johnson and Foote’s paper Designing Reusable Classes, Journal of Object-Oriented Programming , 1988.

El libro del Gang of Four

Richard Sweet ,1983.

John Vlissides column for C++ report

http://www.betaversion.org/%7Estefano/

http://www.laputan.org/drc/drc.html

http://www.laputan.org/dfc/discussion.html#Rock

http://www.digibarn.com/friends/curbow/star/XDEPaper.pdf

http://researchweb.watson.ibm.com/designpatterns/

http://www.flexwiki.com/default.aspx

http://www.artima.com/lejava/articles/patterns_practice.html

http://www.artima.com/index.jsp

http://today.java.net/pub/a/today/2004/02/10/ioc.html

http://www.objectmentor.com/resources/articles/dip.pdf

http://www.objectmentor.com/

One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user’s application code. The framework often plays the role of the main program in coordinating and sequencing application activity. This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application.

Ralph Johnson y Brian Foote

Construyendo bzip2 con mingw

bzip2 es una rutina de compresión de código libre. bzip2 es competitivo con las mejores técnicas estadí­sticas (PPM) en términos de compresión pero mucho más rápido. El código de bzip2 esta escrito en ansi c y no tiene dependencias, así­ que quise usarlo para probar varios enfoques para construir los ejecutables en Windows XP siendo una aplicación cuyo ambiente natural es Linux.

mingw

Una diferencia en el modelo de archivos entre Windows y Unix es como se determina si un archivo es ejecutable o no.

En unix los privilegios de ejecución de un archivo están definidos dentro de la estructura interna del archivo y existen utilerí­as como chmod que permiten manipular los privilegios de ejecución.

En Windows, la extensión de un archivo determina si es un ejecutable o no. los archivo ejecutables en Windows tiene la terminación .exe. Existen otras terminaciones de archivos ejecutables, pero si el archivo tiene un terminación que no este en la lista entonces no es un ejecutable. Dicho de otra manera, En Windows, la terminación de un archivo determina que aplicación (ejecutalbe) esta asociada con él.

Para crear el ejecutable de bizp2 usando mingw

Bajar las fuentes de bzip2
Modificar el archivo Makefile quitando las lineas que contengan la instrucción
chmod a+x
En el shell de msys ir al directorio de los fuentes de bzip2 y ejecutar el comando
make install

Visual Studio 2005 command prompt

En el shell de comandos de Visual Studio 2005 ir al directorio de los fuentes de bzip2 y ejecutar el comando
nmake -f makefile.msc

Al ejecutarse el nmake hay algunos warnings pero los ejecutables se generan bien

Visual Studio 2005 Proyecto Visual C++ win32

Abrir un nuevo proyecto del tipo libreria estatica con el nombre de salida libbz2.lib
Agregar los archivos

blocksort.c
huffman.c
crctable.c
randtable.c
compress.c
decompress.c
bzlib.c
bzlib.h
Construir la libreria
Abrir un nuevo proyecto del tipo consola
Agregar el archivo
bzip2.c
Agregar la referencia a libbz2.lib en los archivos de entrada del linker
construir el ejecutable

Comparación de Java y C#

Hace algunos años , en un lugar de cuyo nombre no quiero acordarme, se discutía el plan quinquenal del programa académicos en tecnologí­a de información. Los administradores del grupo, siguiendo linea del jefe divisional, decidieron normalizar el uso de Java para toda la curricula departamental. Algunos de los profesores expresamos tibias dudas sobre al asunto, pero no hubo realmente opiniones en contra.

A mi el incidente me recordó la tendencia histórica de los ejércitos del mundo a preparase para la guerra anterior sin comprender que el entorno tecnológico y social hacen inoperantes e irrelevantes enfoques que en el pasado hubieran sido determinativos e innovadores. La linea Magniot de los francés como ejemplo concreto.

Aunque Microsoft es más fiero como monopolio que como motor tecnológico, tiene el suficiente peso económico y social para absorber el mejor talento del mundo y el Sr. Puertas ha mostrado saber a donde va el puck.

Java sigue teniendo su lugar como herramienta de desarrollo inter-plataforma pero hay evidencias de que su mejor momento tal vez ya paso. Microsoft tiró el guante blanco al implementar la aplicación de referencia J2EE, PetShop, en .Net y obtuvo mejores números, tanto en lineas de código como en rendimiento. Cada vez que Java levantaba el guante, la evidencia era más contundente: .Net permite desarrollos en menos tiempo y con mejor rendimiento que J2EE.

En el mismo foro de desarrolladores de Java de Sun el consenso en el tema de manejo de genéricos fue que la implementación de C# es superior a la de Java.

Aunque hay diferencias de implementación, Java y C#, tienen puntos en común. Despues de todo, C# es la respuesta de Microsoft a Java. El articulo C# and Java: Comparing Programming Languages presenta una comparación detallada.

Un punto en común entre Java y C# es que no manejan herencia multiple y promueven el uso de interfaces para propositos equivalentes.

El sitio Desarrollo Profesional de Aplicaciones tiene bastante material didactico sobre .Net en español.

Programación orientada a aspectos

Tal vez sea porque estamos a principios de siglo o simplemente un espejismo pero pareciera que estamos en los albores de un cambio paradigmático en el desarrollo de software post orientación a objetos.



Uno de los ideales del desarrollo de software es la capacidad de modificar el funcionamiento de un sistema sin tocar una línea de código. Algunos enfoques en este sentido es inyección de dependencias y programación orientada a aspectos (AOP).

David Hayden en su bitácora presenta un ejemplo, en el contexto de la librería empresarial del grupo de patrones y practicas de Microsoft, del uso del bloque de aplicación de inyección de políticas para guardar registros de llamadas a métodos y se quejaba de que la librería en realidad no soporta el patrón de inyección de dependencias. Lo cual provoco una demostración de las capacidades de Windsor para soportar AOP.

Referencias:

“In-flght” profiling with AOP
Aspect# Integration Facility
Castle
Windsor AOP – Policy Injection Application Block – Best Way to Use Windsor AOP?
Building the Policy Injection in 40 Minutes with Windsor
Castle Windsor AOP – Dependency Injection and Aspect-Oriented Programming – Policy Injection Application Block
Policy Injection Application Block Example
ObjectBuilder is Getting Some Love for the CodePlex Container
.NET Community Downloads and Sample Code