99 bottles of beer in UPC (and C)

Phil Merkey

Introduction

The website http://www.99-bottles-of-beer.net holds a collection of programs that generate the song 99 Bottles of Beer. The collection has programs in more than 990 different languages. The follow is my humble submission to the collection.

<99bottles.c>=
/*
 * UPC (Unified Parallel C: www.upcworld.org) by Phil Merkey
 * To run as C program compile: gcc 99bottles.c
 * To run as UPC program on a Cray X1 
 *   compile: cc -hupc -hssp 99bottles.c
 *   run:     aprun -n <THREADS> ./a.out
 * Other versions of UPC will require the appropriate syntax changes
 * for 'fetch_and_minus'
 */
#include <stdio.h>
#include <time.h>

#define ROUNDS 100
#define VERSE_LEN 132

#if __UPC__

#include <upc.h>
#include <intrinsics.h>
shared long counter;
#define fetch_and_minus(A) _amo_afadd((volatile long *)(A), -1)
shared [] char song[ROUNDS*VERSE_LEN];

#else

#define upc_barrier
#define MYTHREAD 0
#define upc_memput memcpy
long counter;
#define fetch_and_minus(A) serial_fetch_and_add((long *)(A), -1)
char song[ROUNDS*VERSE_LEN];

#endif

main()
{
   int cnt, offset;
   char verse_buf[VERSE_LEN];

   srand48(33);
   if(MYTHREAD == 0) counter = ROUNDS-1;
   upc_barrier;

   while(1){
       cnt = fetch_and_minus( &counter );
       if( cnt < 0 )
           break;
       offset = build_verse( cnt , verse_buf );
       upc_memput( &song[offset], verse_buf, VERSE_LEN );
   }
   upc_barrier;
   if(MYTHREAD == 0 )  printf("%s", (char *) song);
}

/*
 * Note that build_verse pulls the bottles off the wall in
 * order, takes a random amount of time to complete the task of
 * building the verse, then writes it into the correct location 
 * in the song. This allows each thread to contribute to the
 * construction of the song while handling issues of 
 * load-balancing and synchronization in a trivial way.
 */

int build_verse(int cnt, char *verse)
{
    char stash_str[60];
        struct timespec surfeiting;

    report_stash_str(1, cnt, stash_str );
    sprintf(verse,"\n\n%s on the wall, ", stash_str);
    report_stash_str(0, cnt, stash_str );
    sprintf(verse+strlen(verse),"%s.", stash_str);
        surfeiting.tv_sec = 0;
        surfeiting.tv_nsec = (ROUNDS-cnt)*(lrand48() % 100000);
        nanosleep(&surfeiting, NULL);
    if ( cnt ==  0 ){
        sprintf(verse+strlen(verse), "\nGo to the store and buy some more, ");
        report_stash_str(0, ROUNDS-1, stash_str );
        sprintf(verse+strlen(verse), "%s on the wall.\n", stash_str);
     } else {
        sprintf(verse+strlen(verse), "\nTake one down and pass it around, ");
        report_stash_str(0, cnt-1, stash_str );
        sprintf(verse+strlen(verse), "%s on the wall.                 ", stash_str);
    }
    return( (ROUNDS-1-cnt) * VERSE_LEN );
}

report_stash_str(int cap, int nb, char *stash_str )
{
     if( nb == 0 )
        sprintf(stash_str, "%co more bottles of beer", (cap)?'N':'n' );
     else
        sprintf(stash_str, "%d bottle%s of beer", nb, (nb>1)?"s":"");
}

int serial_fetch_and_add( long *cntr, int val )
{
    int ret;
    ret = *cntr;
    *cntr += val;
    return(ret);
}