<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
// +----------------------------------------------------------------------+
// | Copyright (c) 2006 Tom Klingenberg                                   |
// +----------------------------------------------------------------------+
// | license info GPL/LGPL                                                |
// +----------------------------------------------------------------------+
// | Author: Tom Klingenberg <tklingenberg@lastflood.com>                 |
// +----------------------------------------------------------------------+
//
/**
 * ISBN
 *
 * Handle, Convert and Validate ISBN Numbers
 *
 * PHP version 5
 *
 * LICENSE: GPL/LGPL
 *
 * Package to handle, convert and validate ISBN numbers. It includes:
 *     - ISBN specifics:
 *        - EAN/Prefix (integer)
 *        - 'ISBNBody' (string)
 *        - Group/Registration Group [2001: Group identifier] (integer)
 *        (- GroupTitle/Registration Group Title (string))
 *        - 'ISBNSubbody'' (string)
 *        - Publisher/Registrant [2001: Publisher identifier] (string)
 *        - Title/Publication [2001: Title identifier] (string)
 *        - Checkdigit (string)
 *        - Version (integer) value as of a CONST
 *     - Syntactical Validation plus Validation based on real ISBN Data
 *   - ISBN-10 (ISO 2108)
 *      - checksum calculation (ISBN-10)
 *        - validation
 *      - conversion to ISBN-13-978
 *   - ISBN-13-978 (2005 Handbook, ISO pending; ISBN-13)
 *      - checksum calculation (EAN)
 *        - validation
 *
 * Based on standards published by international ISBN Agency
 * http://www.isbn-international.org/
 *
 *
 * @category    UNKNOWN
 * @package        ISBN
 * @author        Tom Klingenberg <tklingenberg@lastflood.com>
 * @copyright    2006 Tom Klingenberg
 * @license        GPL/LGPL
 * @version        0.1.0
 */

// {{{ constants
/**
 * ISBN Versions supported
 */
define("ISBN_VERSION_NONE"false);
/*
 * VERSION_UNKNOWN is by the caller only, this shall never
 * be a value returned by a public function or getter
 */
define("ISBN_VERSION_UNKNOWN"0);
define("ISBN_VERSION_ISBN_10"10);
define("ISBN_VERSION_ISBN_13"13978);
define("ISBN_VERSION_ISBN_13_978"ISBN_VERSION_ISBN_13);
define("ISBN_VERSION_ISBN_13_979"13979);  /* reserved */

/**
 * Default Additonal ISBN (formatting) Values
 */
define("ISBN_DEFAULT_INPUTVERSION"ISBN_VERSION_UNKNOWN);
define("ISBN_DEFAULT_COSMETIC_SEPERATOR""-");
/*
 * ISBN_DEFAULT_PRINT_LANG_SPECIFIC_PREFIX
 *
 * When printed, the ISBN is always preceded by the letters "ISBN".
 * Note: In countries where the Latin alphabet is not used, an abbreviation
 * in the characters of the local script may be used in addition to the
 * Latin letters "ISBN".
 * This can be defined as a default value wihtin this constant.
 */
define("ISBN_DEFAULT_PRINT_LANG_SPECIFIC_PREFIX""");
// }}}

// {{{ ISBN
/**
 * class ISBN
 *
 * @since      Class available since Release 0.0.0
 */
class ISBN
{
    
/**
     * @var string ISBN Registration Group
     */
    
private $isbn_group "";
    
/**
     * @var string ISBN Publisher
     */
    
private $isbn_publisher "";
    
/**
     * @var string ISBN Title
     */
    
private $isbn_title "";

    
/**
     * @var mixed ISBN number version
     */
    
private $ver ISBN_VERSION_NONE;

    
// {{{ __construct
    /**
     * Constructor
     *
     * @param array    $isbn    String of ISBN Value to use
     * @param mixed    $ver    Optional Version Constant
     *
     * @access public
     */
    
function __construct($isbn ""$ver ISBN_DEFAULT_INPUTVERSION) {
        
/* validate & handle optional isbn parameter */
        
if (is_string($isbn) == false ) {
            throw new 
Exception("ISBN parameter must be a string"4);
        }
        if (
strlen($isbn) == 0) {
            
$this->setISBN($isbn);
            return;
        }

        
/* validate version parameter */
        
if (self::_ISBNVersionIs($ver) == false) {
            throw new 
Exception("ISBN Version parameter is not an ISBN Version"3);
        }

        
/* ISBN has been passed, check the version now:
         *     if it is unknown, try to dertine it, if this fails
         *     throw an exception
         */
        
if ($ver == ISBN_VERSION_UNKNOWN) {
            
$verguess self::_ISBNVersionGuess($isbn);
            if (
self::_ISBNVersionIsValid($verguess)) {
                
$ver $verguess;
            } else {
                
// throw new Exception("Unknown ISBN Version could not be guessed.", 5);
                
$ver ISBN_VERSION_NONE;
            }
        }
        
/* version determined */
        
$this->ver $ver;

        
/* handle a complete invalid ISBN of which a version could
         * not be determined. */
        
if ($ver === ISBN_VERSION_NONE) {
            
$this->setISBN("");
            return;
        }

        try {
            
$this->setISBN($isbn);
        } catch(
Exception $e) {
            
/* the isbn is invalid and not set, sothat this
             * ISBN object will be set to a blank value. */
            
$this->setISBN("");
        }

    }
    
// }}}

    // {{{ __extractCheckdigit()
    /**
     *
     * extract Checkdigit of an ISBN-Number
     *
     * @param string    $isbnn    normalized ISBN string
     *
     * @return boolean    False if failed
     * @return string    string containing the ISBN-Body
     *
     */
    
private static function __extractCheckdigit($isbnn) {
        
$checkdigit false;
        
$checkdigit substr($isbnn,-1);
        return (string) 
$checkdigit;
    }
    
// }}}

    // {{{ __extractEANPrefix()
    /**
     * extracts EAN-Prefix of a normalized isbn string
     *
     *
     * @param string    $isbnn    normalized isbn string
     *
     * @return boolean    False if failed
     * @return string    Prefix
     */
    
private static function __extractEANPrefix($isbnn)
    {
        
$r settype($isbnn"string");
        if (
$r === false) {
            return 
false;
        }
        if (
strlen($isbnn) < 3) {
            return 
false;
        }
        
$prefix substr($isbnn03);
        return 
$prefix;
    }
    
// }}}

    // {{{ __extractGroup()
    /**
     * extract Registration Group of an ISBN-Body
     *
     * @param string    $isbnbody    ISBN-Body
     *
     * @return boolean    false if failed
     * @return integer    Value of Registration Group
     */
    
private static function __extractGroup($isbnbody) {
        
$group '';
        
$subbody '';
        
$r self::__ISBNBodyParts($isbnbody$group$subbody);
        if (
$r === false) {
            return 
false;
        }
        return 
$group;
    }
    
// }}}

    // {{{ __extractISBNBody()
    /**
     * extract ISBN-Body of an ISBN-Number
     *
     * @param string    $isbnn    normalized ISBN string
     *
     * @return boolean    False if failed
     * @return string    string containing the ISBN-Body
     */
    
private static function __extractISBNBody($isbnn) {
        
/* extract */
        
$body false;
        
$isbnn = (string) $isbnn;
        
$l strlen($isbnn);
        if (
$l == 10) {
            
$body =  substr($isbnn0, -1);
        } elseif (
$l == 13) {
            
$body =  substr($isbnn3, -1);
        } else {
            return 
false;
        }
        
/* verify */
        
$r settype($body"string");
        if (
$r === false) {
            return 
false;
        }
        if (
strlen($body) != 9) {
            return 
false;
        }
        if (
ctype_digit($body) === false) {
            return 
false;
        }
        return 
$body;
    }
    
// }}}

    // {{{ __ISBNBodyParts()
    /**
     * Get the 2 Parts of the ISBN-Body (ISBN-10/ISBN-13-978)
     *
     * @param string    $isbnbody            ISBN-Body
     * @param string    &$registrationgroup    Registration Group
     * @param string    &$isbnsubbody        ISBN-Subbody
     *
     * @return boolean    False if failed, True on success
     *
     * @access private
     */
    
private static function __ISBNBodyParts($isbnbody, &$registrationgroup, &$isbnsubbody) {
        
/* validate input (should not be needed, @access private) */
        
$r settype($isbnbody"string");
        if (
$r === false) {
            return 
false;
        }
        if (
strlen($isbnbody) != 9) {
            return 
false;
        }
        if (
ctype_digit($isbnbody) === false) {
            return 
false;
        }
        
/* extract registraion group
         * boundaries see p.13 2005 handbook
         */
        
$boundaries = array();
        
$boundaries[] = array(    0599991);
        
$boundaries[] = array(60000600993); // Iran 2006-12-05
        
$boundaries[] = array(60100699990);
        
$boundaries[] = array(70000799991);
        
$boundaries[] = array(80000949992);
        
$boundaries[] = array(95000989993);
        
$boundaries[] = array(99000998994);
        
$boundaries[] = array(99900999995);
        
/* segment value */
        
$segment substr($isbnbody05);
        
$segmentvalue intval($segment);
        
/* test segment value against boundaries */
        
$r false;
        foreach (
$boundaries as $boundary) {
            if (
$segmentvalue >= $boundary[0] && $segmentvalue <= $boundary[1]) {
                
$r $boundary[2];
            }
        }
        if (
$r === false) {
            return 
false;
        }
        
/* $r is 0 when the boundary is not defined */
        
if ($r === 0) {
            return 
false;
        }
        
$registrationgroup substr($isbnbody0$r);
        
$isbnsubbody substr($isbnbody$r);
        return 
true;
    }
    
// }}}

    // {{{ __ISBNSubbodyParts()
    /**
     * Get the 2 Parts of the ISBN-Subbody (ISBN-10/ISBN-13)
     *
     * @param string    $isbnsubbody    ISBN-Subbody
     * @param integer    $groupid        Registrationgroup
     * @param string    &$registrant    Registrant
     * @param string    &$publication    Publication
     *
     * @return boolean    False if failed, true on success
     *
     * @access private
     */
    
private static function __ISBNSubbodyParts($isbnsubbody$groupid, &$registrant, &$publication) {
        
/* validate input (should not be needed, @access private) */
        
$r settype($isbnsubbody"string");
        if (
$r === false) {
            return 
false;
        }
        
$l strlen($isbnsubbody);
        if ( 
$l || $l 8) {
            return 
false;
        }
        if (
ctype_digit($isbnsubbody) === false) {
            return 
false;
        }
        
$r settype($groupid'integer');
        if (
$r === false) {
            return 
false;
        }
        if (
$groupid || $groupid 99999) {
            return 
false;
        }
        
/* extract registrant based on group and registrant range
         * parse this specific group format: array("English speaking area",
         *   "00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999");
         */
        
$group self::__getISBN10Group($groupid);
        if (
$group === false) {
            return 
false;
        }
        
$len self::__getRegistrantLength($isbnsubbody$group[1]);
        if (
$len === false) {
            return 
false;
        }
        
$registrant substr($isbnsubbody0$len);
        
$publication substr($isbnsubbody$len);
        return 
true;
    }
    
// }}}

    // {{{ __getRegistrantLength()
    /*
     * Return Length of Registrant part within an ISBNSubbody in a specific
     * grouprange in this specific format:
     *
     * "00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999"
     *
     * Info: This function is compatible with Groupranges formatted in the
     * .js file and might become obsolete if new formats are more fitting.
     *
     * @param string    $isbnsubbody
     * @param string    $grouprange        Grouprange in the Format "#a1-#z1;#a2-z2[...]"
     *
     * @return boolean    False if failed
     * @return int        Length (in chars) of Registrant
     *
     * @access private
     */
    
private static function __getRegistrantLength($isbnsubbody$grouprange) {
        
$r settype($grouprange'string');
        if (
$r === false) {
            return 
false;
        }
        if (
strlen($grouprange) < 3) {
            return 
false;
        }
        
$sl strlen($isbnsubbody);
        
$ranges explode(";"$grouprange);
        foreach(
$ranges as $range) {
            
$range trim($range);
            
$fromto explode("-"$range);
            if (
count($fromto) !== 2) {
                return 
false;
            }
            
/* validation:
             * from and to need to be in the same class,
             * having the same length.
             * registrant can not be bigger or same then the
             * whole subbody, at least there is one digit for
             * the publication.
             */

            
$l strlen($fromto[0]);
            if (
$l != strlen($fromto[1])) {
                return 
false;
            }
            if (
$l >= $sl) {
                return 
false;
            }

            
/* check that from/to values are in order */
            
if (strcmp($fromto[0], $fromto[1]) >= 0) {
                return 
false;
            }

            
/* compare and fire if matched */
            
$compare intval(substr($isbnsubbody0$l));

            if (
strcmp($fromto[0], $compare) < && strcmp($fromto[1], $compare) > -1) {
                return 
$l;
            }
        }
        return 
false;
    }
    
// }}}

    // {{{ __getISBN10Group()
    /**
     * Get ISBN-10 Registration Group Data by its numeric ID
     *
     * @param integer    $id    Registration Group Identifier
     *
     * @return mixed    array:     group array
     *                    boolean: False if failed
     */
    
private static function __getISBN10Group($id) {
        
$r settype($id"integer");
        if (
$r === false) {
            return 
false;
        }
        
$groups self::__getISBN10Groups();
        if (
$groups === false) {
            return 
false;
        }
        if (isset(
$groups[$id]) === false) {
            return 
false;
        }
        
$group $groups[$id];
        return 
$group;
    }
    
// }}}

    // {{{ __getISBN10Groups()
    /**
     * Get all ISBN-10 Registration Groups
     *
     * @return array    groups array
     *
     * Info: This function connects outer world data into this class logic
     *       which can be generated with the supplied tools.
     *         A user should not alter the array data. This data should be altered
     *         together with the international ISBN Agency only.
     */
    
private static function __getISBN10Groups() {
        
$groups = array();
        
/* ISBN Code-Generator - Ranges */
        
$groups[0]     = array("English speaking area"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999");
        
$groups[1]     = array("English speaking area"    "00-09;100-399;4000-5499;55000-86979;869800-998999");
        
$groups[2]     = array("French speaking area"    "00-09;10-19;200-349;35000-39999;400-699;7000-8399;84000-89999;900000-949999;9500000-9999999");
        
$groups[3]     = array("German speaking area"    "00-02;030-033;0340-0369;03700-03999;04-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999");
        
$groups[4]     = array("Japan"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999");
        
$groups[5]     = array("Russian Federation"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-909999;91000-91999;9200-9299;93000-94999;9500-9799;98000-98999;9900000-9909999;9910-9999");
        
$groups[600]   = array("Iran"    "00-09;100-499;5000-8999;90000-99999");
        
$groups[7]     = array("China, People's Republic"    "00-09;100-499;5000-7999;80000-89999;900000-999999");
        
$groups[80]    = array("Czech Republic; Slovakia"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-999999");
        
$groups[81]    = array("India"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-999999");
        
$groups[82]    = array("Norway"    "00-09;10-19;200-699;7000-8999;90000-98999;990000-999999");
        
$groups[83]    = array("Poland"    "00-09;10-19;200-599;60000-69999;7000-8499;85000-89999;900000-999999");
        
$groups[84]    = array("Spain"    "00-09;10-19;200-699;7000-8499;85000-89999;9000-9199;920000-923999;92400-92999;930000-949999;95000-96999;9700-9999");
        
$groups[85]    = array("Brazil"    "00-09;10-19;200-599;60000-69999;7000-8499;85000-89999;900000-979999;98000-99999");
        
$groups[86]    = array("Serbia and Montenegro"    "00-09;10-29;300-599;6000-7999;80000-89999;900000-999999");
        
$groups[87]    = array("Denmark"    "00-09;10-29;400-649;7000-7999;85000-94999;970000-999999");
        
$groups[88]    = array("Italian speaking area"    "00-09;10-19;200-599;6000-8499;85000-89999;900000-949999;95000-99999");
        
$groups[89]    = array("Korea"    "00-09;10-24;250-549;5500-8499;85000-94999;950000-999999");
        
$groups[90]    = array("Netherlands, Belgium (Flemish)"    "00-09;10-19;200-499;5000-6999;70000-79999;800000-849999;8500-8999;900000-909999;940000-949999");
        
$groups[91]    = array("Sweden"    "0-1;20-49;500-649;7000-7999;85000-94999;970000-999999");
        
$groups[92]    = array("International Publishers (Unesco, EU), European Community Organizations"    "0-5;60-79;800-899;9000-9499;95000-98999;990000-999999");
        
$groups[93]    = array("India - no ranges fixed yet"    "");
        
$groups[950]   = array("Argentina"    "00-09;10-49;500-899;9000-9899;99000-99999");
        
$groups[951]   = array("Finland"    "0-1;20-54;550-889;8900-9499;95000-99999");
        
$groups[952]   = array("Finland"    "00-19;200-499;5000-5999;60-65;6600-6699;67000-69999;7000-7999;80-94;9500-9899;99000-99999");
        
$groups[953]   = array("Croatia"    "0-0;10-14;150-599;6000-9499;95000-99999");
        
$groups[954]   = array("Bulgaria"    "00-09;10-29;300-799;8000-8999;90000-92999;9300-9999");
        
$groups[955]   = array("Sri Lanka"    "0-0;1000-1999;20-54;550-799;8000-9499;95000-99999");
        
$groups[956]   = array("Chile"    "00-09;10-19;200-699;7000-9999");
        
$groups[957]   = array("Taiwan, China"    "00-02;0300-0499;05-19;2000-2099;21-27;28000-30999;31-43;440-819;8200-9699;97000-99999");
        
$groups[958]   = array("Colombia"    "00-09;10-59;600-799;8000-9499;95000-99999");
        
$groups[959]   = array("Cuba"    "00-09;10-19;200-699;7000-8499");
        
$groups[960]   = array("Greece"    "00-09;10-19;200-659;6600-6899;690-699;7000-8499;85000-99999");
        
$groups[961]   = array("Slovenia"    "00-09;10-19;200-599;6000-8999;90000-94999");
        
$groups[962]   = array("Hong Kong"    "00-09;10-19;200-699;7000-8499;85000-86999;8700-8999;900-999");
        
$groups[963]   = array("Hungary"    "00-09;10-19;200-699;7000-8499;85000-89999;9000-9999");
        
$groups[964]   = array("Iran"    "00-09;10-14;150-249;2500-2999;300-549;5500-8999;90000-96999;970-989;9900-9999");
        
$groups[965]   = array("Israel"    "00-09;10-19;200-599;7000-7999;90000-99999");
        
$groups[966]   = array("Ukraine"    "00-19;2000-2999;300-699;7000-8999;90000-99999");
        
$groups[967]   = array("Malaysia"    "0-5;60-89;900-989;9900-9989;99900-99999");
        
$groups[968]   = array("Mexico"    "01-09;10-39;400-499;5000-7999;800-899");
        
$groups[969]   = array("Pakistan"    "0-1;20-39;400-799;8000-9999");
        
$groups[970]   = array("Mexico"    "01-09;10-59;600-899;9000-9099;91000-96999;9700-9999");
        
$groups[971]   = array("Philippines?"    "000-019;02-02;0300-0599;06-09;10-49;500-849;8500-9099;91000-99999");
        
$groups[972]   = array("Portugal"    "0-1;20-54;550-799;8000-9499;95000-99999");
        
$groups[973]   = array("Romania"    "0-0;100-169;1700-1999;20-54;550-759;7600-8499;85000-88999;8900-9499;95000-99999");
        
$groups[974]   = array("Thailand"    "00-09;10-19;200-699;7000-8499;85000-89999;90000-94999;9500-9999");
        
$groups[975]   = array("Turkey"    "00000-00999;01-09;10-24;250-599;6000-9199;92000-98999;990-999");
        
$groups[976]   = array("Caribbean Community"    "0-3;40-59;600-799;8000-9499;95000-99999");
        
$groups[977]   = array("Egypr"    "00-09;10-19;200-499;5000-6999;700-999");
        
$groups[978]   = array("Nigeria"    "000-199;2000-2999;30000-79999;8000-8999;900-999");
        
$groups[979]   = array("Indonesia"    "000-099;1000-1499;15000-19999;20-29;3000-3999;400-799;8000-9499;95000-99999");
        
$groups[980]   = array("Venezuela"    "00-09;10-19;200-599;6000-9999");
        
$groups[981]   = array("Singapore"    "200-299;3000-9999;00-09");
        
$groups[982]   = array("South Pacific"    "00-09;100-699;70-89;9000-9999");
        
$groups[983]   = array("Malaysia"    "00-01;020-199;2000-3999;40000-49999;50-79;800-899;9000-9899;99000-99999");
        
$groups[984]   = array("Bangladesh"    "00-09;10-39;400-799;8000-8999;90000-99999");
        
$groups[985]   = array("Belarus"    "00-09;10-39;400-599;6000-8999;90000-99999");
        
$groups[986]   = array("Taiwan, China"    "120-559;5600-7999;80000-99999;00-09;10-11");
        
$groups[987]   = array("Argentina"    "00-09;1000-1999;20000-29999;30-49;500-899;9000-9499;95000-99999");
        
$groups[988]   = array("Hongkong"    "00-09;10-19;200-799;8000-9699;97000-99999");
        
$groups[989]   = array("Portugal"    "0-1;20-54;550-799;8000-9499;95000-99999");
        
$groups[9940]  = array("Montenegro"    "0-1;20-49;500-899;9000-9999");
        
$groups[9941]  = array("Georgia"    "0-0;10-39;400-899;9000-9999");
        
$groups[9942]  = array("Ecuador"    "00-89;900-994;9950-9999");
        
$groups[9943]  = array("Uzbekistan"    "00-29;300-399;4000-9999");
        
$groups[9944]  = array("Turkey"    "0-2;300-499;5000-5999;60-89;900-999");
        
$groups[9945]  = array("Dominican Republic"    "00-09;10-39;400-849;8500-9999");
        
$groups[9946]  = array("Korea, P.D.R."    "0-1;20-39;400-899;9000-9999");
        
$groups[9947]  = array("Algeria"    "0-1;20-79;800-999");
        
$groups[9948]  = array("United Arab Emirates"    "00-09;10-39;400-849;8500-9999");
        
$groups[9949]  = array("Estonia"    "0-0;10-39;400-899;9000-9999");
        
$groups[9950]  = array("Palestine"    "00-29;300-840;8500-9999");
        
$groups[9951]  = array("Kosova"    "00-09;10-39;400-849;8500-9999");
        
$groups[9952]  = array("Azerbaijan"    "0-1;20-39;400-799;8000-9999");
        
$groups[9953]  = array("Lebanon"    "0-0;10-39;400-599;60-89;9000-9999");
        
$groups[9954]  = array("Morocco"    "0-1;20-39;400-799;8000-9999");
        
$groups[9955]  = array("Lithuania"    "00-09;10-39;400-929;9300-9999");
        
$groups[9956]  = array("Cameroon"    "0-0;10-39;400-899;9000-9999");
        
$groups[9957]  = array("Jordan"    "00-09;10-39;400-849;8500-9999");
        
$groups[9958]  = array("Bosnia and Herzegovina"    "0-0;10-49;500-899;9000-9999");
        
$groups[9959]  = array("Libya"    "0-1;20-79;800-949;9500-9999");
        
$groups[9960]  = array("Saudi Arabia"    "00-09;10-59;600-899;9000-9999");
        
$groups[9961]  = array("Algeria"    "0-2;30-69;700-949;9500-9999");
        
$groups[9962]  = array("Panama"    "00-09;10-54;5500-5599;56-59;600-849;8500-9999");
        
$groups[9963]  = array("Cyprus"    "0-2;30-54;550-749;7500-9999");
        
$groups[9964]  = array("Ghana"    "0-6;70-94;950-999");
        
$groups[9965]  = array("Kazakhstan"    "00-09;10-39;400-899;9000-9999");
        
$groups[9966]  = array("Kenya"    "00-09;10-69;7000-7499;750-959;9600-9999");
        
$groups[9967]  = array("Kyrgyzstan"    "00-09;10-39;400-899;9000-9999");
        
$groups[9968]  = array("Costa Rica"    "00-09;10-49;500-939;9400-9999");
        
$groups[9970]  = array("Uganda"    "00-09;10-39;400-899;9000-9999");
        
$groups[9971]  = array("Singapore"    "0-5;60-89;900-989;9900-9999");
        
$groups[9972]  = array("Peru"    "00-09;1;200-249;2500-2999;30-59;600-899;9000-9999");
        
$groups[9973]  = array("Tunisia"    "0-0;10-69;700-969;9700-9999");
        
$groups[9974]  = array("Uruguay"    "0-2;30-54;550-749;7500-9499;95-99");
        
$groups[9975]  = array("Moldova"    "0;100-399;4000-4499;45-89;900-949;9500-9999");
        
$groups[9976]  = array("Tanzania"    "0-5;60-89;900-989;9990-9999");
        
$groups[9977]  = array("Costa Rica"    "00-09;10-89;900-989;9900-9999");
        
$groups[9978]  = array("Ecuador"    "00-09;10-29;300-399;40-94;950-989;9900-9999");
        
$groups[9979]  = array("Iceland"    "0-4;50-75;760-899;9000-9999");
        
$groups[9980]  = array("Papua New Guinea"    "0-3;40-89;900-989;9900-9999");
        
$groups[9981]  = array("Morocco"    "00-09;100-159;1600-1999;20-79;800-949;9500-9999");
        
$groups[9982]  = array("Zambia"    "00-09;10-79;800-889;9900-9999");
        
$groups[9983]  = array("Gambia"    "80-94;950-989;9900-9999");
        
$groups[9984]  = array("Latvia"    "00-09;10-49;500-899;9000-9999");
        
$groups[9985]  = array("Estonia"    "0-4;50-79;800-899;9000-9999");
        
$groups[9986]  = array("Lithuania"    "00-09;10-39;400-899;9000-9399;940-969;97-99");
        
$groups[9987]  = array("Tanzania"    "00-09;10-39;400-879;8800-9999");
        
$groups[9988]  = array("Ghana"    "0-2;30-54;550-749;7500-9999");
        
$groups[9989]  = array("Macedonia"    "0-0;100-199;2000-2999;30-59;600-949;9500-9999");
        
$groups[99901] = array("Bahrain"    "00-09;10-49;500-799;80-99");
        
$groups[99902] = array("Gabon - no ranges fixed yet"    "");
        
$groups[99903] = array("Mauritius"    "0-1;20-89;900-999");
        
$groups[99904] = array("Netherlands Antilles; Aruba, Neth. Ant"    "0-5;60-89;900-999");
        
$groups[99905] = array("Bolivia"    "0-3;40-79;800-999");
        
$groups[99906] = array("Kuwait"    "0-2;30-59;600-699;70-89;9-9");
        
$groups[99908] = array("Malawi"    "0-0;10-89;900-999");
        
$groups[99909] = array("Malta"    "0-3;40-94;950-999");
        
$groups[99910] = array("Sierra Leone"    "0-2;30-89;900-999");
        
$groups[99911] = array("Lesotho"    "00-09;10-59;600-999");
        
$groups[99912] = array("Botswana"    "0-3;400-599;60-89;900-999");
        
$groups[99913] = array("Andorra"