// green_bottles.cpp // by jono #include #include #include #include using std::ostringstream; using std::cout; using std::vector; using std::string; using std::endl; const unsigned MAX_DECIMATE = 1000000000; const unsigned MAX_BOTTLES = 0 - 1; struct BottlesOfBeer { BottlesOfBeer(unsigned n, bool cap = false) :_(n) ,cap_(cap) {} const unsigned _; const bool cap_; }; string englishNumber(unsigned n) { ostringstream os; vector result; unsigned decimate = MAX_DECIMATE; static const char *ONES[] = { "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" }; static const char *TENS[] = { "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" }; static const char *THOUS[] = { "", "thousand", "million", "billion" }; bool keep = false; for(unsigned tmp = n; decimate > 0; tmp = tmp%decimate, decimate /= 10) { keep = keep ? keep : (tmp >= decimate); if(keep) { unsigned unit = (tmp - tmp%decimate)/decimate; result.push_back(unit); } } bool and = false, space = false, comma = false; unsigned tens = 0; //tens buffer unsigned decades = result.size(); vector::iterator it = result.begin(); for(int cnt = 1; it != result.end(); it++, cnt++) { if(!(tens || *it)) continue; if(comma)os << ','; comma = false; if(space)os << ' '; space = false; switch((decades-cnt)%3) { case 0: { unsigned number = tens + *it; if(!number) continue; if(number && and) os << " and ", and = false; if(number < 20)os << ONES[number]; else { unsigned ones = number%10; os << TENS[number/10]; if(ones) os << ' ' << ONES[ones]; } unsigned dec = (decades-cnt)/3; if(dec)os << ' '; os << THOUS[dec]; comma = true; space = true; } break; case 1: tens = *it * 10; continue; case 2: if(*it) os << ONES[*it] << ' ' << "hundred"; and = true; break; default: break; } } os << std::ends; return os.str(); } inline std::ostream &operator<<(std::ostream &os, const BottlesOfBeer &bottles) { static unsigned nCache = 0; static string sCache; if(!nCache || nCache != bottles._) if(!bottles._)sCache = "no more "; else sCache = englishNumber(bottles._), nCache = bottles._; if(!sCache.length()) throw "a banana"; os << char(bottles.cap_ ? sCache.at(0) - 32 : sCache.at(0)) << sCache.substr(1).c_str(); os << " bottle"; if(bottles._ != 1) os << 's'; os << " of beer"; return os; } struct Bar { Bar(unsigned capacity) : shelving_((capacity <= MAX_BOTTLES) ? capacity : MAX_BOTTLES) , stock_(shelving_) {} inline void open(bool never_close = false); private: const unsigned shelving_; unsigned stock_; }; inline void Bar::open(bool never_close) { static const char coda[] = " on the wall"; static const char next[] = "Take one down and pass it around, "; static const char last[] = "Go to the store and buy some more, "; for(;; stock_--) { std::cout << std::endl << BottlesOfBeer(stock_, true) << coda << ", " << BottlesOfBeer(stock_) << '.' << std::endl << (stock_?next:last) << BottlesOfBeer(stock_?stock_-1:shelving_) << coda << '.' << std::endl ; if(!stock_) if(never_close)stock_ = shelving_; else break; } } int main(int argc, char* argv[]) { Bar bar(0 - 1); bar.open(true); return 0; }