Simple algorithm to insert commas (commaize) in 9 languages


This project contains nine different language implementations of a simple algorithm named “commaize” that inserts commas into a number. The languages are: bash, c++, java, javascript, perl, php, python, ruby and tcl. My hope is that it will be a useful reference.

Contents

  1. Introduction
  2. Downloads
  3. Download Files
  4. Installation
  5. Bash Implementation
  6. C++ Implementation
  7. Java Implementation
  8. Javascript Implementation
  9. Perl Implementation
  10. PHP Implementation
  11. Python Implementation
  12. Ruby Implementation
  13. Tcl Implementation
  14. Tested Platforms
  15. License

Introduction

I created it because I sometimes have to work on code written in a variety of languages. Having an example like this is really helpful as a reference for languages that I rarely use (like tcl). Note that because these implementations are meant to act as a reference, they are not optimized.

This project contains the algorithm coded in complete standalone programs. Each program prints a similar list of formatted data in 3 columns. The first column is the test number, the second is the language specific format of the input and the third column is the output. That third column must be identical for all programs. In some cases there is a fourth column to remind me of what the row is for. This is what the output looks like for the python implementation (commaize.py):

$ ./commaize.py
   1                    1                     1
   2                   12                    12
   3                  123                   123
   4                 1234                 1,234
   5                12345                12,345
   6               123456               123,456
   7              1234567             1,234,567
   8             12345678            12,345,678
   9            123456789           123,456,789
  10                 1.23                  1.23
  11                12.34                 12.34
  12               123.45                123.45
  13              1234.56              1,234.56
  14             12345.67             12,345.67
  15            123456.78            123,456.78
  16           1234567.89          1,234,567.89
  17           12345678.9          12,345,678.9
  18                  1.0                     1
  19                   -1                    -1
  20                  -12                   -12
  21                 -123                  -123
  22                -1234                -1,234
  23               -12345               -12,345
  24              -123456              -123,456
  25             -1234567            -1,234,567
  26            -12345678           -12,345,678
  27           -123456789          -123,456,789
  28                -1.23                 -1.23
  29               -12.34                -12.34
  30              -123.45               -123.45
  31             -1234.56             -1,234.56
  32            -12345.67            -12,345.67
  33           -123456.78           -123,456.78
  34          -1234567.89         -1,234,567.89
  35          -12345678.9         -12,345,678.9
  36                 -1.0                    -1
  37            123456789           123,456,789
  38           12345678,9          12.345.678,9  # EU style
  39            12345.123             12,345.12  # 2 decimal places
  40                12345             12,345.00  # 2 decimal places
  41           12345.1251             12,345.13  # 2 decimal places
  42           1234567.89           1,234,567.9  # 1 decimal place

Note that there are two compiled languages: c++ and java so there is a Makefile that will run the compilations. You will have to edit it to pick up the correct compiler. When you do you will see that I used gcc-4.8.2 installed locally on my Mac as described here: http://joelinoff.com/blog/?p=1003 because I wanted the latest C++-11 features.

The javascript implementation requires browser rendering so it has an associated commaize.html file.

All of the source code is freely available based on the MIT license. See the LICENSE.txt file for details.

Downloads

All of the implementations are available to download. There are three archive formats: tar.bz2, tar.gz and zip.

File Size Checksum Extract Command
commaize.tar.bz2 13K 60400 tar jxf commaize.tar.bz2
commaize.tar.gz 14K 5113 tar zxf commaize.tar.gz
commaize.zip 21K 54782 unzip commaize.zip

Download Files

The archives contain the following files in a local commaize directory.

File Description
commaize.html HTML file that references commaize.js.
commaize.cc The C++ implementation. Uses some features from C++11.
commaize.java The java (1.6) implementation.
commaize.js The javascript implementation.
commaize.pl The perl (5.12) implementation.
commaize.php The php (5.4) implementation.
commaize.py The python (2.7) implementation.
commaize.rb The ruby (0.9.6, 1.8.7) implementation.
commaize.sh The bash implementation.
commaize.tcl The tcl (8.6) implementation.
golden.txt The golden output for testing.
LICENSE.txt The source code license information.
Makefile Builds and tests the implementations. Here are some interesting targets.

  1. all – check, build and test
  2. build – build the executables for C++ and java
  3. check – check the language compilers/interpreters
  4. clean – clean up
  5. test – run the tests

You will need to modify this file for your environment.

README.txt Information about the project.

Installation

The package is available in 3 formats: bzipped tar format, gzipped tar format and zip format. The following example shows how to download, install and run the project using the bzipped tar format.

$ # Installation example.
$ # There are three ways to download the project.
$ wget http://projects.joelinoff.com/commaize/commaize.tar.bz2
$ wget http://projects.joelinoff.com/commaize/commaize.tar.gz
$ wget http://projects.joelinoff.com/commaize/commaize.zip

$ # There are three ways to extract the project.
$ tar jxf commaize.tar.bz2
$ #tar zxf commaize.tar.gz
$ #unzip commaize.zip

$ # Here is the basic recipe but note that you MUST edit the
$ # Makefile to point to the correct local c++ compiler.
$ cd commaize
$ edit Makefile  # change the GXX compiler references
$ make
  [output snipped]
  # ================================================================
  # test
  # ================================================================
    1  test-commaize.class.out          passed
    2  test-commaize.exe.out            passed
    3  test-commaize.php.out            passed
    4  test-commaize.pl.out             passed
    5  test-commaize.py.out             passed
    6  test-commaize.rb.out             passed
    7  test-commaize.sh.out             passed
    8  test-commaize.tcl.out            passed
  
  TOTAL:      8
  PASSED:     8
  FAILED:     0

Bash Implementation

This is the bash implementation.

#!/bin/bash

#
# Insert thousands separator into a number and, optionally, specify
# the number of decimal places.
#
# Here are some examples that demonstrate how it is used.
#
#    x=$(commaize 1234)                      # 1,234
#    x=$(commaize -1234)                     # -1,234
#    x=$(commaize 12345.678)                 # 12,345.678
#    x=$(commaize -12345.678)                # -12,345.678
#    x=$(commaize 12345.678 2)               # 12,345.69
#    x=$(commaize 12345.678 1)               # 12,345.7
#    x=$(commaize 1234 -1 ".")               # 1.234
#    x=$(commaize "1234567,89" -1 "." ",")   # EU format: 1.234.567,89
#
# The arguments are:
#
#   $1 == number
#   $2 == decimal places (default: -1 -> do nothing)
#   $3 == thousands separator (default: ",")
#   $4 == decimal point (default: ".")
#
function commaize() {
    local num="$1"
    local dpl=-1
    local sep=","
    local dpt="."
    if [[ "$2" != "" ]] ; then dpl=$2 ; fi
    if [[ "$3" != "" ]] ; then sep="$3" ; fi
    if [[ "$4" != "" ]] ; then dpt="$4" ; fi

    if (( $dpl >= 0 )) ; then
        fmt="%.${dpl}f"
        num=$(printf "$fmt" $num)
    fi

    local neg=""
    if [[ "${num:0:1}" == "-" ]] ; then
        neg="-"
        # Strip off the leading minus sign.
        numl=$((${#num} - 1))
        num=${num:1:$numl}
    fi

    local parts=($(echo "$num" | tr "$dpt" "\n"))
    local nparts=${#parts[@]}
    if (( $nparts > 2 )) ; then
        echo "ERROR: invalid string: $num"
        exit 1
    fi

    # Insert the separators.
    characteristic=${parts[0]}
    result=''
    j=$(( ${#characteristic} % 3 ))
    for (( i=0; i<${#characteristic}; i++ )); do
        k=$(( $i % 3))
        if (( $k == $j )) && (( $i > 0 )) ; then
            result="$result$sep"
        fi
        digit=${characteristic:$i:1}
        result="$result$digit"
    done

    # There was a decimal point, append
    # the mantissa.
    if (( $nparts == 2 )) ; then
        mantissa="${parts[1]}"
        if (( $dpl < 0 )) ; then
            # Strip off trailing zeros if dpl was not not specified.
            # Convert 1.0 --> 1
            mantissa=$(echo "$mantissa" | sed -e 's/0$//g')
        fi
        if (( ${#mantissa} > 0 )) ; then
            result="$result$dpt$mantissa"
        fi
    fi
    echo "$neg$result"
}

# Main
idx=0
data=(1 12 123 1234 12345 123456 1234567 12345678 123456789
    1.23 12.34 123.45 1234.56 12345.67 123456.78 1234567.89 12345678.9
    1.0
    -1 -12 -123 -1234 -12345 -123456 -1234567 -12345678 -123456789
    -1.23 -12.34 -123.45 -1234.56 -12345.67 -123456.78 -1234567.89 -12345678.9
    -1.0
    '123456789')
for datum in ${data[@]}; do
    result=$(commaize $datum)
    idx=$(($idx + 1))
    printf "%4d %20s  %20s\n" $idx $datum $result
done

datum="12345678,9"
result=$(commaize $datum -1 '.' ',')
idx=$(($idx + 1))
printf "%4d %20s  %20s  # EU style\n" $idx $datum $result

data=(12345.123 12345 12345.1251)
for datum in ${data[@]}; do
    result=$(commaize $datum 2)
    idx=$(($idx + 1))
    printf "%4d %20s  %20s  # 2 decimal places\n" $idx $datum $result
done

datum=1234567.89
result=$(commaize $datum 1)
idx=$(($idx + 1))
printf "%4d %20s  %20s  # 1 decimal place\n" $idx $datum $result

C++ Implementation

This is the C++ implementation. It uses C++-11 constructs so it will not work on platforms that do not have a recent C++ compiler. In my case I had to install an updated version of the C++ compiler to handle C++-11 constructs from http://joelinoff.com/blog/?p=1003.

This is how I compiled it.

$ # First install the compiler. The default is too old.
$ # You can skip this step if you have newer version.
$ cd ~/work
$ mkdir -p gcc/4.8.2
$ cd gcc/4.8.2
$ wget http://projects.joelinoff.com/gcc-4.8.2./Makefile
<output snipped>
$ wget http://projects.joelinoff.com/gcc-4.8.2./bld.sh
<output snipped>
$ make
<output snipped>

$ # Now compile your program.
$ cd ~/work/commaize
$ CXX_RTFDIR=~/work/gcc/4.8.2/rtf
$ export PATH="${CXX_RTFDIR}/bin:${PATH}"
$ export LD_LIBRARY_PATH="$(CXX_RTFDIR)/lib:$(CXX_RTFDIR)/lib64:${LD_LIBRARY_PATH}"
$ export LD_RUN_PATH="${CXX_RTFDIR}/lib:${CXX_RTFDIR}/lib64:${LD_LIBRARY_PATH}"
$ g++ --version  # make sure that you have the correct version
<output snipped>
$ g++ -g -Wall -std=c++11 -o commaize.exe commaize.cc
<output snipped>

$ # Run it.
$ ./commaize.exe
<output snipped>

This is the source code.

// ================================================================
// Commaize a number.
// ================================================================
#include <cstdlib>
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;

template <typename T> string commaize_tostring(T in) {return to_string(in);}
string commaize_tostring(const string& in) {return in;}

/**
 * Insert thousands separator into a number and, optionally, specify
 * the number of decimal places.
 * 
 * Here are some examples that demonstrate how it is used.
 * 
 *   string x;
 *   x=commaize(1234)                            # 1,234
 *   x=commaize(-1234)                           # -1,234
 *   x=commaize(12345.678)                       # 12,345.678
 *   x=commaize(-12345.678)                      # -12,345.678
 *   x=commaize(12345.678, dpl=2)                # 12,345.69
 *   x=commaize(12345.678, dpl=1)                # 12,345.7
 *   x=commaize(1234,sep='.')                    # 1.234
 *   x=commaize("1234567,89", sep='.', dpt=',')  # EU: 1.234.567,89
 * 
 * The arguments are:
 * 
 * @param num  The number: int, float or string.
 * @param dpl  Decimal places (default: -1 -> do nothing).
 * @param sep  Thousands separator (default: ",").
 * @param dpt  Decimal point (default: ".").
 * @returns a formatted string.
 */
template <typename T>
string commaize(T num, int dpl=-1, char sep=',', char dpt='.')
{
  string characteristic = commaize_tostring(num);
  if (dpl >= 0) {
    // Use sprintf() to do the rounding.
    double val = atof(characteristic.c_str());
    char fmt[128];
    char tmp[128];
    sprintf(fmt, "%%.%df", dpl);
    sprintf(tmp, fmt, val);
    characteristic = tmp;
  }

  string neg="";
  if (characteristic[0] == '-') {
    neg = "-";
    characteristic = characteristic.substr(1, string::npos);
  }

  string mantissa = "";
  size_t pos = characteristic.find(dpt);
  if (pos != string::npos) {
    // There is a decimal point, break out
    // the characteristic and the mantissa
    // because we only care about the
    // characteristic for commas. We will
    // append the mantissa at the end.
    mantissa = characteristic.substr(pos+1);

    // Trim trailing zeros.
    if (dpl < 0) {
      while (mantissa.size() > 0 && mantissa[mantissa.size()-1] == '0') {
        mantissa = mantissa.substr(0, mantissa.size() - 1);
      }
    }
    characteristic = characteristic.substr(0, pos);
  }

  // Insert the commas.
  size_t j = characteristic.size() % 3;
  string result = "";
  result.reserve(characteristic.size() + ((characteristic.size()/3) + 2 + mantissa.size()));
  for(size_t i=0; i<characteristic.size(); i++) {
    if ((i%3) == j && i > 0) {
      result += sep;
    }
    result += characteristic[i];
  }
  if (!mantissa.empty()) {
    char dpts[2] = {dpt, 0};
    result += string(dpts) + mantissa;
  }
  return neg + result;
}

/**
 * main
 */
int main()
{
  string result;
  string str;
  unsigned id = 0;

  // Test positive integers.
  size_t nums[] = {1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789};
  for(auto num : nums) {
    result = commaize(num);
    cout  << setw(4) << right << ++id << " "
          << right << setw(20) << num
          << "   "
          << right << setw(20) << result
          << endl;
  }

  // Test positive doubles.
  double dnums[] = {1.23, 12.34, 123.45, 1234.56, 12345.67, 123456.78, 1234567.89, 12345678.9, 1.0};
  for(auto dnum : dnums) {
    result = commaize(dnum);
    cout  << setw(4) << right << ++id << " "
          << right << fixed << setw(20) << dnum
          << "   "
          << right << setw(20) << result
          << endl;
  }

  // Test negative integers.
  for(long num: nums) {
    num = -num;
    result = commaize(num);
    cout  << setw(4) << right << ++id << " "
          << right << setw(20) << num
          << "   "
          << right << setw(20) << result
          << endl;
  }

  // Test negative doubles.
  for(auto num: dnums) {
    num = -num;
    result = commaize(num);
    cout  << setw(4) << right << ++id << " "
          << right << fixed << setprecision(2) << setw(20) << num
          << "   "
          << right << setw(20) << result
          << endl;
  }

  // String test.
  str = "123456789";
  result = commaize(str);
  cout  << setw(4) << right << ++id << " "
        << right << setw(20) << str
        << "   "
        << right << setw(20) << result
        << endl;

  // EU style
  str = "12345678,9";
  result = commaize(str, -1, '.', ',');
  cout  << setw(4) << right << ++id << " "
        << right << fixed << setprecision(2) << setw(20) << str
        << "   "
        << right << setw(20) << result
        << "  # EU style"
        << endl;

  // dollarize
  // Round to 2 significant digits.
  double ddata[] = {12345.123, 12345, 12345.1251};
  for(auto datum : ddata) {
    result = commaize(datum, 2);
    cout  << setw(4) << right << ++id << " "
          << right << fixed << setw(20) << datum
          << "   "
          << right << setw(20) << result
          << "  # 2 decimal places"
          << endl;
  }

  // 1 decimal place.
  string datum = "1234567.89";
  result = commaize(datum, 1);
  cout  << setw(4) << right << ++id << " "
        << right << fixed << setw(20) << datum
        << "   "
        << right << setw(20) << result
        << "  # 1 decimal place"
        << endl;
}

Java Implementation

This is the java implementation. You compile it as follows:

$ javac Commaize.java
<output snipped>
$ java Commaize
<output snipped>

Here is the source code.

// Commaize a number.
import java.util.*;

public class Commaize {

    /**
     * doubleToString
     */
    public static String doubleToString(double in) {
        Double d = in;
        int di = d.intValue();
        if (d == di) {
            // Optimization for easy floats.
            return String.format("%d", di);
        }
        
        String floatnum = String.format("%f", d);
        
        // Chop off trailing zeros.
        int i = floatnum.length() - 1;
        while (i >= 0 && floatnum.charAt(i) == '0') {
            i--;
        }
        floatnum = floatnum.substring(0, i+1);
        
        // Chop off the period if there is no mantissa.
        i = floatnum.length() - 1;
        if (i >= 0 && floatnum.charAt(i) == '.') {
            floatnum = floatnum.substring(0, i);
        }
        
        return floatnum;
    }
    
    /**
     * Insert thousands separator into a number and, optionally, specify
     * the number of decimal places.
     * 
     * Here are some examples that demonstrate how it is used.
     * 
     *   string x;
     *   x=commaize(1234)                        # 1,234
     *   x=commaize(-1234)                       # -1,234
     *   x=commaize(12345.678)                   # 12,345.678
     *   x=commaize(-12345.678)                  # -12,345.678
     *   x=commaize(12345.678, 2)                # 12,345.69
     *   x=commaize(12345.678, 1)                # 12,345.7
     *   x=commaize(1234, -1, '.')               # 1.234
     *   x=commaize("1234567,89", -1, '.', ',')  # EU: 1.234.567,89
     * 
     * The arguments are:
     * 
     * @param num  The number: int, float or string.
     * @param dpl  Decimal places (default: -1 -> do nothing).
     * @param sep  Thousands separator (default: ",").
     * @param dpt  Decimal point (default: ".").
     * @returns a formatted string.
     */
    public static  String commaize(T num, int dpl, char sep, char dpt) {
        String rep = num.toString();
        if (rep.indexOf('E') >= 0) {
            rep = doubleToString(Double.parseDouble(rep));
        }

        if (dpl > 0) {
            // Take advantage of built in rounding.
            String fmt = String.format("%%.%df", dpl);
            double value = Double.parseDouble(rep);
            rep = String.format(fmt, value);
        }

        // Get the parts of the number (characteristic and mantissa).
        //   123.45
        //   ^  ^^
        //   |  |+--- mantissa
        //   |  +---- decimal point
        //   +------- characteristic
        String characteristic = "";
        String mantissa = "";
        boolean past_dpt = true;
        for (int i=0; i= 0 && mantissa.charAt(i) == '0') {
                i--;
            }
            
            mantissa = mantissa.substring(0, i+1);
            i = mantissa.length() - 1;
            if (i >= 0 && mantissa.charAt(i) == dpt) {
                // Eliminate the decimal point separater if there are no
                // trailing values (e.g. "1.").
                mantissa = "";
            }
        }

        // Handle negative numbers.
        String neg = "";
        if (characteristic.charAt(0) == '-') {
            neg = "-";
            characteristic = characteristic.substring(1);
        }

        // Insert the command moving from left to right.
        String result = "";
        int offset = characteristic.length() % 3;  // offset to first comma
        for(int i=0; i 0)) {
                result += sep;
            }
            result += characteristic.charAt(i);
        }

        rep = neg + result + mantissa;
        return rep;
    }
    public static  String commaize(T num) {
        return commaize(num, -1, ',', '.');
    }
    public static  String commaize(T num, int dpl) {
        return commaize(num, dpl, ',', '.');
    }

    /**
     * main.
     */
    public static void main(String[] args) {
        int id = 0;
        String result;
        String str;
        String msg;

        // Test positive integers.
        int inums[] = new int[] {1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789};
        for(int val : inums) {
            result = commaize(val);
            msg = String.format("%4d %20d %20s", ++id, val, result);
            System.out.println(msg);
        }

        // Test positive floating points.
        double dnums[] = new double[] {1.23, 12.34, 123.45, 1234.56, 12345.67, 123456.78, 1234567.89, 12345678.9, 1.0};
        for(double val : dnums) {
            str = doubleToString(val);
            result = commaize(val);
            msg = String.format("%4d %20s %20s", ++id, str, result);
            System.out.println(msg);
        }

        // Test negative integers.
        for(int val : inums) {
            int nval = -val;
            result = commaize(nval);
            msg = String.format("%4d %20d %20s", ++id, nval, result);
            System.out.println(msg);
        }

        // Test negative floating points.
        for(double val : dnums) {
            double nval = -val;
            result = commaize(nval);
            str = doubleToString(nval);
            msg = String.format("%4d %20s %20s", ++id, str, result);
            System.out.println(msg);
        }

        // String test.
        str = "123456789";
        result = commaize(str);
        msg = String.format("%4d %20s %20s", ++id, str, result);
        System.out.println(msg);

        // EU style
        str = "12345678,9";
        result = commaize(str, -1, '.', ',');
        msg = String.format("%4d %20s %20s", ++id, str, result);
        System.out.println(msg);

        // dollarize
        // Round to 2 significant digits.
        double ddata[] = new double[] {12345.123, 12345, 12345.1251};
        for(double val : ddata) {
            str = doubleToString(val);
            result = commaize(val, 2);
            msg = String.format("%4d %20s %20s", ++id, str, result);
            System.out.println(msg);
        }

        // 1 decimal place
        str = "1234567.89";
        result = commaize(str, 1);
        msg = String.format("%4d %20s %20s", ++id, str, result);
        System.out.println(msg);
    }
}

Javascript Implementation

This is the javascript implementation. It consists of two parts: an HTML wrapper and the javascript code. You can run it in your browser by navigating to the HTML page or you can use a local javascript testing environment like rhino.

When you run it in your browser the output looks like this:

Screen Shot 2014-03-29 at 9.11.19 AM

Here is the HTML source code.

<!DOCTYPE HTML>
<html>
  <head>
    <title>commaize</title>
    <script type="text/javascript" src="commaize.js"></script>
  </head>
  <body>
    <div id="place">
    </div>
  </body>
</html>

Here is the javascript source code. Note that this implementation deliberately avoids the use of libraries like jquery. For a production system you definitely want to use javascript libraries to make your code easier to read and more maintainable.

// This is raw javascript on purpose.
window.onload = function() {
    // I would normally recommend using jquery ready() but since I
    // want to keep this example as simple as possible, jquery is
    // assumed to not be available.


    // ================================================================
    // Test a condition and report a message.
    // ================================================================
    function assert(condition, description)
    {
        if(!condition) {
            alert('ASSERTION: ' + description + ': ' + condition);
        }
    }
    
    // ================================================================
    // Commaize a number.
    //    Insert thousands separator into a number and, optionally, specify
    //    the number of decimal places.
    //    
    //    Here are some examples that demonstrate how it is used.
    //    
    //      x=commaize(1234)                        # 1,234
    //      x=commaize(-1234)                       # -1,234
    //      x=commaize(12345.678)                   # 12,345.678
    //      x=commaize(-12345.678)                  # -12,345.678
    //      x=commaize(12345.678, 2)                # 12,345.69
    //      x=commaize(12345.678, 1)                # 12,345.7
    //      x=commaize(1234, -1, '.')               # 1.234
    //      x=commaize("1234567,89", -1, '.', ',')  # EU: 1.234.567,89
    //    
    //    The arguments are:
    //    
    //    @param num  The number: int, float or string.
    //    @param dpl  Decimal places (default: -1 -> do nothing).
    //    @param sep  Thousands separator (default: ",").
    //    @param dpt  Decimal point (default: ".").
    //    @returns a formatted string.
    // ================================================================
    function commaize(num, dpl=-1, sep=',', dpt='.')
    {
        var snum = num.toString();
        assert(snum.indexOf(sep) < 0, 'found "' + sep + '" in "' + snum + '"');

        // Format to N decimal places.
        if (dpl > 0) {
            snum = parseFloat(snum).toFixed(dpl).toString();
        }

        // Split into parts.
        var parts = snum.split(dpt);  // 123 or 123.45
        assert( parts.length < 3, 'too many parts: ' + parts.length);

        // Get the characteristic and mantissa.
        //   123.45
        //   ^  ^^
        //   |  |+--- mantissa
        //   |  +---- decimal point
        //   +------- characteristic
        var characteristic = parts[0];
        var mantissa = parts.length == 1 ? '' : dpt + parts[1];

        // Handle negative numbers.
        var neg = '';
        if (characteristic[0] == '-') {
            neg = '-';
            characteristic = characteristic.slice(1);  // strip off the negative
        }

        // Clean up the mantissa. Remove trailing zeros unless a specific
        // number of decimal places (dpl) was specified.
        //    1.000 > 1
        if (mantissa.length && dpl < 0) {
            // Make sure that 1.0 --> 1
            while (mantissa.slice(-1) == '0') {
                mantissa = mantissa.slice(0, -1);
            }
            if (mantissa == dpt) {
                mantissa = "";
            }
        }

        // Insert the commas moving from left to right.
        var result = '';  // where to put the result
        var offset = characteristic.length % 3  // offset to start inserting commas
        for(var i=0; i<characteristic.length; i++) {
            if ((i%3) == offset && i) {
                // if we are at the digit where a separator needs to be
                // pre-pended, prepend the separator.
                result += sep ;
            }
            result += characteristic[i];
        }

        return neg + result + mantissa;
    }

    // ================================================================
    // main
    // ================================================================
    function main()
    {
        // This function will generate a table on the page with the
        // filled in contents of the commaize function for the examples
        // that we are interested in.
        var rowidx = 0;
        row = function(val, result, msg='')
        {
            var s1 = "" + rowidx;
            var s2 = "" + val;
            var s3 = "" + result;
            var html = '';

            rowidx++;

            // Pad.
            while (s1.length < 4) {s1 = ' ' + s1;}
            while (s2.length < 20) {s2 = ' ' + s2;}
            while (s3.length < 20) {s3 = ' ' + s3;}

            html += s1 + ' ' + s2 + '  ' + s3;
            if (msg.length > 0) {
                html += '  ' + msg;
            }
            html += '\n';
            
            return html;
        }

        var place = document.getElementById('place');
        var html = '';
        html += '<pre>\n';
        html += 'BEGIN\n';

        // Rows of data here.
        var data = [1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789,
                    1.23, 12.34, 123.45, 1234.56, 12345.67, 123456.78, 1234567.89, 12345678.9,
                    1.0,
                    -1, -12, -123, -1234, -12345, -123456, -1234567, -12345678, -123456789,
                    -1.23, -12.34, -123.45, -1234.56, -12345.67, -123456.78, -1234567.89, -12345678.9,
                    -1.0,
                    '123456789',
                   ];
        for(var i=0; i<data.length; i++) {
            var datum = data[i];
            var result = commaize(datum);
            html += row(datum, result);
        }

        // EU style
        var datum = "12345678,9";
        var result = commaize(datum, -1, '.', ',');  // EU style
        html += row(datum, result, '# EU style');

        // dollarize
        // Round to 2 significant digits.
        var data1 = [12345.123, 12345, 12345.1251,];
        for(var i=0; i<data1.length; i++) {
            datum = data1[i];
            result = commaize(datum, 2);
            html += row(datum, result, '# 2 decimal places');
        }

        // 1 decimal place
        datum = "1234567.89";
        result = commaize(datum, 1)
        html += row(datum, result, '# 1 decimal place');

        html += 'END\n';
        html += '</pre>\n';
        place.innerHTML = html;
    }

    main();
}

Perl Implementation

This is the perl implementation.

#!/usr/bin/env perl
#
# Example that shows how to insert commas into a number.
#
use strict;
use warnings;

&main;

# ================================================================
# main
# ================================================================
sub main
{
    my @data = (1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789,
		1.23, 12.34, 123.45, 1234.56, 12345.67, 123456.78, 1234567.89, 12345678.9,
		1.0,
		-1, -12, -123, -1234, -12345, -123456, -1234567, -12345678, -123456789,
		-1.23, -12.34, -123.45, -1234.56, -12345.67, -123456.78, -1234567.89, -12345678.9,
		-1.0,
		'123456789',
	);

    my $idx = 1;
    foreach my $datum (@data) {
        my $result = &commaize($datum);
	printf("%4d %20s  %20s\n", $idx++, $datum, $result);
    }

    # EU style
    my $datum = "12345678,9";
    my $result = &commaize($datum, -1, ".", ",");  # EU style
    printf("%4d %20s  %20s  # EU style\n", $idx++, $datum, $result);

    # dollarize
    # Round to 2 significant digits.
    my @ddata = (12345.123, 12345, 12345.1251,);
    for $datum (@ddata) {
	$result = &commaize($datum, 2);
        printf("%4d %20s  %20s  # 2 decimal places\n", $idx++, $datum, $result);
    }

    # 1 decimal place
    $datum = "1234567.89";
    $result = &commaize($datum, 1);
    printf("%4d %20s  %20s  # 1 decimal place\n",$idx++, $datum, $result);
}

# ================================================================
# Insert thousands separator into a number and, optionally, specify
# the number of decimal places.
#    
# Here are some examples that demonstrate how it is used.
#    
#   x=commaize(1234)                        # 1,234
#   x=commaize(-1234)                       # -1,234
#   x=commaize(12345.678)                   # 12,345.678
#   x=commaize(-12345.678)                  # -12,345.678
#   x=commaize(12345.678, 2)                # 12,345.69
#   x=commaize(12345.678, 1)                # 12,345.7
#   x=commaize(1234, -1, ".")               # 1.234
#   x=commaize("1234567,89", -1, ".", ",")  # EU: 1.234.567,89
#    
# The arguments are:
#    
#   @param num  The number: int, float or string.
#   @param dpl  Decimal places (default: -1 -> do nothing).
#   @param sep  Thousands separator (default: ",").
#   @param dpt  Decimal point (default: ".").
#   @returns a formatted string.
# ================================================================
sub commaize
{
    my $num = shift;
    my $dpl = shift;
    my $sep = shift;
    my $dpt = shift;

    $dpl = -1  if( !defined($dpl) );
    $sep = "," if( !defined($sep) );
    $dpt = "." if( !defined($dpt) );

    # Format to N decimal places.
    my $rep = $num;
    if ($dpl >= 0) {
	my $fmt = "%.${dpl}f";
	$rep = sprintf($fmt, $num);
    }

    # Split into parts.
    # Using split is tricky here because the split value is unknown,
    # i work around that using this simple brute force approach.
    my @parts = ();
    my @chars = split(//, $rep);
    my $idx = 0;
    for my $char (@chars) {
	if ($char eq $dpt) {
	    $idx++;
	}
	else {
	    if ($#parts <= $idx) {
		push(@parts, "");
	    }
	    $parts[$idx] .= $char;
	}
    }

    # Get the characteristic and mantissa.
    #   123.45
    #   ^  ^^
    #   |  |+--- mantissa
    #   |  +---- decimal point
    #   +------- characteristic
    my $characteristic = $parts[0];
    my $mantissa = "";
    $mantissa = "$dpt$parts[1]" if $#parts > 1;

    # Handle negative numbers.
    my $neg = "";
    if (substr($characteristic, 0, 1) eq "-") {
	$neg = "-";
	$characteristic = substr($characteristic, 1);
    }

    # Insert the commas moving from left to right.
    my $result = "";  # where to put the result
    my $offset = length($characteristic) % 3;  # offset to start inserting commas
    for(my $i=0; $i<length($characteristic); $i++) {
	if (($i % 3) == $offset && $i) {
	    $result .= $sep;
	}
	$result .= substr($characteristic, $i, 1);
    }

    my $final = $neg . $result . $mantissa;
    return $final;
}

PHP Implementation

This is the PHP implementation. This is how you run it.

$ php -f commaize.php

This is the source code.

<?php
#
# Example that shows how to insert commas into a number.
#

# ================================================================
# Insert thousands separator into a number and, optionally, specify
# the number of decimal places.
#    
# Here are some examples that demonstrate how it is used.
#    
#   x=commaize(1234)                            # 1,234
#   x=commaize(-1234)                           # -1,234
#   x=commaize(12345.678)                   # 12,345.678
#   x=commaize(-12345.678)                  # -12,345.678
#   x=commaize(12345.678, 2)                # 12,345.69
#   x=commaize(12345.678, 1)                # 12,345.7
#   x=commaize(1234, -1, ".")               # 1.234
#   x=commaize("1234567,89", -1, ".", ",")  # EU: 1.234.567,89
# 
# The arguments are:
#    
# @param num  The number: int, float or string.
# @param dpl  Decimal places (default: -1 -> do nothing).
# @param sep  Thousands separator (default: ",").
# @param dpt  Decimal point (default: ".").
# @returns a formatted string.
# ================================================================
function commaize($num, $dpl=-1, $sep=",", $dpt=".") {
    $rep = $num;

    # Format to N decimal places.
    if ($dpl >= 0) {
        $fmt = "%.${dpl}f";
        $rep = sprintf($fmt, $num);
    }

    # Split into parts.
    $parts = explode($dpt, $rep);


    # Get the characteristic and mantissa.
    #   123.45
    #   ^  ^^
    #   |  |+--- mantissa
    #   |  +---- decimal point
    #   +------- characteristic
    $characteristic = $parts[0];
    $mantissa = count($parts) == 1 ? "" : $dpt . $parts[1];

    # Handle negative numbers.
    $neg = "";
    if ($characteristic[0] == "-") {
        $neg = "-";
        $characteristic = substr($characteristic, 1);
    }

    # Clean up the mantissa. Remove trailing zeros unless a specific
    # number of decimal places (dpl) was specified.
    #    1.000 > 1
    if (strlen($mantissa) and $dpl < 0) {
        # Make sure that 1.0 --> 1
        $mantissa = rtrim($mantissa, "0");
        $mantissa = rtrim($mantissa, $dpt);
    }

    # Insert the commas moving from left to right.
    $result = "";  # where to put the result
    $offset = strlen($characteristic) % 3;  # offset to start inserting commas
    for($i=0; $i<strlen($characteristic); $i++) {
        if (($i%3) == $offset and $i > 0) {
            $result .= $sep;
        }
        $result .= $characteristic[$i];
    }

    $final = $neg . $result . $mantissa;
    return $final;
}

# ================================================================
# main
# ================================================================
function main() {
    $data = array (1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789,
                   1.23, 12.34, 123.45, 1234.56, 12345.67, 123456.78, 1234567.89, 12345678.9,
                   1.0,
                   -1, -12, -123, -1234, -12345, -123456, -1234567, -12345678, -123456789,
                   -1.23, -12.34, -123.45, -1234.56, -12345.67, -123456.78, -1234567.89, -12345678.9,
                   -1.0,
                   '123456789',
                   );
    $idx = 1;
    foreach($data as $datum) {
        $result = commaize($datum);
        echo sprintf("%4d %20s  %20s\n", $idx++, "$datum", $result);
    }

    # EU style
    $datum = "12345678,9";
    $result = commaize($datum, -1, ".", ",");
    echo sprintf("%4d %20s  %20s  # EU style\n", $idx++, "$datum", $result);

    # dollarize
    # Round to 2 significant digits.
    $data = array(12345.123, 12345, 12345.1251,);
    foreach($data as $datum) {
        $result = commaize($datum, 2);
        echo sprintf("%4d %20s  %20s  # 2 decimal places\n", $idx++, "$datum", $result);
    }

    # 1 decimal place
    $datum = "1234567.89";
    $result = commaize($datum, 1);
    echo sprintf("%4d %20s  %20s  # 1 decimal place\n", $idx++, "$datum", $result);
}

main();
?>

Python Implementation

This is the python implementation. Note that I use asserts because they are available natively.

#!/usr/bin/env python
'''
Example that shows how to insert commas into a number.
'''
import re


def commaize(num, dpl=-1, sep=',', dpt='.'):
    '''
    Insert thousands separator into a number and, optionally, specify
    the number of decimal places.
    
    Here are some examples that demonstrate how it is used.
    
      x=commaize(1234)                            # 1,234
      x=commaize(-1234)                           # -1,234
      x=commaize(12345.678)                       # 12,345.678
      x=commaize(-12345.678)                      # -12,345.678
      x=commaize(12345.678, dpl=2)                # 12,345.69
      x=commaize(12345.678, dpl=1)                # 12,345.7
      x=commaize(1234,sep='.')                    # 1.234
      x=commaize("1234567,89", sep='.', dpt=',')  # EU: 1.234.567,89
    
    The arguments are:
    
    @param num  The number: int, float or string.
    @param dpl  Decimal places (default: -1 -> do nothing).
    @param sep  Thousands separator (default: ",").
    @param dpt  Decimal point (default: ".").
    @returns a formatted string.
    '''
    rep = str(num)  # convert the int or float number to a string
    assert rep.find(sep) < 0  # no separators allowed

    # Format to N decimal places.
    if dpl >= 0:
        fmt = '%.' + str(dpl) + 'f'
        rep = fmt % (float(num))

    # Split into parts.
    parts = rep.split(dpt)  # 123 or 123.45
    assert len(parts) < 3   # multiple dpts not allowed

    # Get the characteristic and mantissa.
    #   123.45
    #   ^  ^^
    #   |  |+--- mantissa
    #   |  +---- decimal point
    #   +------- characteristic
    characteristic = parts[0]
    mantissa = "" if len(parts) == 1 else dpt + parts[1]

    # Handle negative numbers.
    neg = ''
    if characteristic[0] == '-':
        neg = '-'
        characteristic = characteristic[1:]  # strip off the negative

    # Check our assumption that both the characteristic and the mantissa
    # are integers at this point.
    assert re.match(r'^\d+$', characteristic)  # empty strings are not allowed
    if len(mantissa):  # empty strings are allowed
        assert re.match(r'^' + dpt + '\d+$', mantissa)

    # Clean up the mantissa. Remove trailing zeros unless a specific
    # number of decimal places (dpl) was specified.
    #    1.000 > 1
    if len(mantissa) and dpl < 0:
        # Make sure that 1.0 --> 1
        mantissa = mantissa.rstrip('0')
        mantissa = mantissa.rstrip(dpt)

    # Insert the commas moving from left to right.
    result = ''  # where to put the result
    offset = len(characteristic) % 3  # offset to start inserting commas
    for i in range(len(characteristic)):
        if (i%3) == offset and i:
            # if we are at the digit where a separator needs to be
            # pre-pended, prepend the separator.
            result += sep 
        result += characteristic[i]

    final = neg + result + mantissa
    return final


def main():
    '''
    Test the method.
    '''
    idx = [0]
    def incidx():
        idx[0] += 1
        return idx[0]

    data = [1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789,
            1.23, 12.34, 123.45, 1234.56, 12345.67, 123456.78, 1234567.89, 12345678.9,
            1.0,
            -1, -12, -123, -1234, -12345, -123456, -1234567, -12345678, -123456789,
            -1.23, -12.34, -123.45, -1234.56, -12345.67, -123456.78, -1234567.89, -12345678.9,
            -1.0,
            '123456789',
            ]
    for datum in data:
        result = commaize(datum)
        print('%4d %20s  %20s' % (incidx(), str(datum), result))

    # EU style
    datum = "12345678,9"
    result = commaize(datum, sep='.', dpt=',')  # EU style
    print('%4d %20s  %20s  # EU style' % (incidx(), str(datum), result))

    # dollarize
    # Round to 2 significant digits.
    data = [12345.123, 12345, 12345.1251,]
    for datum in data:
        result = commaize(datum, dpl=2)
        print('%4d %20s  %20s  # 2 decimal places' % (incidx(), str(datum), result))

    # 1 decimal place
    datum = "1234567.89"
    result = commaize(datum, dpl=1)
    print('%4d %20s  %20s  # 1 decimal place' % (incidx(), str(datum), result))


if __name__ == '__main__':
    main()

Ruby Implementation

This is the ruby implementation.

#!/usr/bin/env ruby

# ================================================================
# Insert thousands separator into a number and, optionally, specify
# the number of decimal places.
#    
# Here are some examples that demonstrate how it is used.
#    
#   x=commaize(1234)                        # 1,234
#   x=commaize(-1234)                       # -1,234
#   x=commaize(12345.678)                   # 12,345.678
#   x=commaize(-12345.678)                  # -12,345.678
#   x=commaize(12345.678, 2)                # 12,345.69
#   x=commaize(12345.678, 1)                # 12,345.7
#   x=commaize(1234, -1, '.')               # 1.234
#   x=commaize("1234567,89", -1, '.', ',')  # EU: 1.234.567,89
#    
# The arguments are:
#    
#   @param num  The number: int, float or string.
#   @param dpl  Decimal places (default: -1 -> do nothing).
#   @param sep  Thousands separator (default: ",").
#   @param dpt  Decimal point (default: ".").
#   @returns a formatted string.
# ================================================================
def commaize(num, dpl=-1, sep=',', dpt='.')
  snum = num.to_s
  raise "invalid number format" unless snum.count(sep.to_s) == 0

  # Format to N decimal places.
  if (dpl >= 0) then
    fmt = '%.' << dpl.to_i.to_s << 'f'
    snum = sprintf fmt, snum.to_f
  end

  # Split into parts.
  parts = snum.split(dpt)
  raise "invalid number of parts: %d" % (parts.length) unless parts.length < 3

  # Get the characteristic and mantissa.
  #   123.45
  #   ^  ^^
  #   |  |+--- mantissa
  #   |  +---- decimal point
  #   +------- characteristic
  characteristic = parts[0]
  mantissa = parts.length > 1 ? dpt + parts[1] : ""

  # Handle negative numbers.
  neg = ""
  if (characteristic[0] == '-')
    characteristic = characteristic[1..-1]
    neg = "-"
  end

   # Clean up the mantissa. Remove trailing zeros unless a specific
  # number of decimal places (dpl) was specified.
  #    1.000 > 1
  if (mantissa.length > 0 and dpl < 0) then
    # Make sure that 1.0 --> 1
    while (mantissa[-1] == '0')
      mantissa = mantissa.chomp("0")
    end
    mantissa = mantissa.chomp(dpt)
  end

  # Insert the commas moving from left to right.
  result = ""
  j = characteristic.length % 3
  characteristic.split("").each_with_index do |item, i|
    if ((i%3) == j and i > 0) then
      # if we are at the digit where a separator needs to be
      # pre-pended, prepend the separator.
      result << sep
    end
    result << item
  end

  if (mantissa.length) then
    result << mantissa
  end

  return neg + result
end

# ================================================================
# Test method.
# ================================================================
def test()
  data = [1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789,
          1.23, 12.34, 123.45, 1234.56, 12345.67, 123456.78, 1234567.89, 12345678.9,
          1.0,
          -1, -12, -123, -1234, -12345, -123456, -1234567, -12345678, -123456789,
          -1.23, -12.34, -123.45, -1234.56, -12345.67, -123456.78, -1234567.89, -12345678.9,
          -1.0,
          '123456789',
         ]
  idx = 1
  incidx = lambda do
    idx += 1
    return idx
  end
  data.each do |num|
    result = commaize(num)
    print("%4d %20s  %20s\n" % [incidx.call(), num.to_s, result])
  end

  # EU style
  datum = "12345678,9"
  result = commaize(datum, -1, '.', ',')  # EU style
  print("%4d %20s  %20s  # EU style\n" % [incidx.call(), datum.to_s, result])

  # dollarize
  # Round to 2 significant digits.
  data = [12345.123, 12345, 12345.1251,]
  data.each do |num|
    result = commaize(num, 2)
    print("%4d %20s  %20s  # 2 decimal places\n" % [incidx.call(), num.to_s, result])
  end
  
  # 1 decimal place
  datum = "1234567.89"
  result = commaize(datum, dpl=1)
  print("%4d %20s  %20s  # 1 decimal place\n" % [incidx.call(), datum.to_s, result])

end

if __FILE__ == $0
  test()
end

Tcl Implementation

This is the tcl implementation.

#!/usr/bin/env tclsh
#
# Example that shows how to insert commas into a number.
#

# ================================================================
# Insert thousands separator into a number and, optionally, specify
# the number of decimal places.
#    
# Here are some examples that demonstrate how it is used.
#    
#   set x [commaize 1234]                  # 1,234
#   set x [commaize -1234]                 # -1,234
#   set x [commaize 12345.678]             # 12,345.678
#   set x [commaize -12345.678]            # -12,345.678
#   set x [commaize 12345.678 2]           # 12,345.69
#   set x [commaize 12345.678 1]           # 12,345.7
#   set x [commaize 1234 -1 "."]           # 1.234
#   set x [commaize "1234567,89" "." ","]  # EU: 1.234.567,89
# 
# The arguments are:
#    
# @param num  The number: int, float or string.
# @param dpl  Decimal places (default: -1 -> do nothing).
# @param sep  Thousands separator (default: ",").
# @param dpt  Decimal point (default: ".").
# @returns a formatted string.
# ================================================================
proc commaize {num {dpl -1} {sep ","} {dpt "."}} {
    set rep $num

    # Format to N decimal places.
    if {$dpl >= 0} {
        set fmt "%.${dpl}f"
        set rep [format $fmt $num]
    }

    # Split into parts.
    set parts [split $rep $dpt]
    set size [llength $parts]

    # Get the characteristic and mantissa.
    #   123.45
    #   ^  ^^
    #   |  |+--- mantissa
    #   |  +---- decimal point
    #   +------- characteristic
    set characteristic [lindex $parts 0]
    if {$size == 1} {
        set mantissa ""
    } else {
        set num [lindex $parts 1]
        set mantissa "${dpt}${num}"
    }

    # Trim off trailing zeros.
    if {$dpl < 0} {
        set mantissa [string trimright $mantissa "0"]
        set mantissa [string trimright $mantissa $dpt]
    }

    # Handle negative numbers.
    set neg [string index $characteristic 0]
    if {[string compare $neg "-"] == 0} {
        # strip off the negative
        set characteristic [string range $characteristic 1 [string length $characteristic]]
    } else {
        set neg ""
    }

    # Insert the commas moving from left to right.
    set result ""
    set offset [expr [string length $characteristic] % 3]
    for {set i 0} {$i < [string length $characteristic]} {incr i} {
        if {[expr $i>0] && [expr ($i%3)] == $offset} {
            append result $sep
        }
        append result [string index $characteristic $i]
    }

    set final "${neg}${result}${mantissa}"
    return $final
}

# ================================================================
# main
# ================================================================
proc main {} {
    set data [list 1 12 123 1234 12345 123456 1234567 12345678 123456789 \
            1.23 12.34 123.45 1234.56 12345.67 123456.78 1234567.89 12345678.9 \
            1.0 \
            -1 -12 -123 -1234 -12345 -123456 -1234567 -12345678 -123456789 \
            -1.23 -12.34 -123.45 -1234.56 -12345.67 -123456.78 -1234567.89 -12345678.9 \
            -1.0 \
            "123456789" \
            ]

    set idx 0
    foreach datum $data {
        incr idx
        set result [commaize $datum]
        set f [format "%4d %20s %20s" $idx $datum $result]
        puts $f
    }

    # EU style
    set datum "12345678,9"
    incr idx
    set result [commaize $datum -1 "." ","]
    set f [format "%4d %20s %20s" $idx $datum $result]
    puts $f

    # dollarize
    # Round to 2 significant digits
    set data [list 12345.123 12345 12345.1251]
    foreach datum $data {
        incr idx
        set result [commaize $datum 2]
        set f [format "%4d %20s %20s" $idx $datum $result]
        puts $f
    }

    # 1 decimal place
    set datum "1234567.89"
    incr idx
    set result [commaize $datum 1]
    set f [format "%4d %20s %20s" $idx $datum $result]
    puts $f
}

main

Tested Platforms

I have tested this on the following platforms. In all cases I had to install an updated versions of the C++ compiler to handle C++-11 constructs from http://joelinoff.com/blog/?p=1003.

Platform Status> Notes
CentOS 5.5 Passed Required installion of g++ 4.8.2
CentOS 5.8 Passed Required installation of g++ 4.8.2
CentOS 6.5 Passed Required installation of g++ 4.8.2
Mac OS X 10.9.2 Passed Required installation of g++ 4.8.2

License

This software is licensed under the MIT open source license.

Copyright (c) 2014 by Joe Linoff

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Enjoy!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.