//
//  NBPhoneNumberUtil.m
//  libPhoneNumber
//
//  Created by tabby on 2015. 2. 8..
//  Copyright (c) 2015년 ohtalk.me. All rights reserved.
//

#import "NBPhoneNumberUtil.h"
#import "NBPhoneNumber.h"
#import "NBNumberFormat.h"
#import "NBPhoneNumberDesc.h"
#import "NBPhoneMetaData.h"
#import "NBMetadataHelper.h"
#import <math.h>

#if TARGET_OS_IPHONE
    #import <CoreTelephony/CTTelephonyNetworkInfo.h>
    #import <CoreTelephony/CTCarrier.h>
#endif


#pragma mark - NBPhoneNumberUtil interface -

@interface NBPhoneNumberUtil ()
{
    NSMutableDictionary *entireStringRegexCache;
    NSLock *entireStringCacheLock;
    NSMutableDictionary *regexPatternCache;
    NSLock *lockPatternCache;
}

@property (nonatomic, strong, readwrite) NSMutableDictionary *i18nNumberFormat;
@property (nonatomic, strong, readwrite) NSMutableDictionary *i18nPhoneNumberDesc;
@property (nonatomic, strong, readwrite) NSMutableDictionary *i18nPhoneMetadata;

#if TARGET_OS_IPHONE
@property (nonatomic, strong) CTTelephonyNetworkInfo *telephonyNetworkInfo;
#endif

@end


@implementation NBPhoneNumberUtil

#pragma mark - Static Int variables -
const static NSUInteger NANPA_COUNTRY_CODE_ = 1;
const static int MIN_LENGTH_FOR_NSN_ = 2;
const static int MAX_LENGTH_FOR_NSN_ = 16;
const static int MAX_LENGTH_COUNTRY_CODE_ = 3;
const static int MAX_INPUT_STRING_LENGTH_ = 250;

#pragma mark - Static String variables -
static NSString *VALID_PUNCTUATION = @"-x‐-―−ー－-／ ­​⁠　()（）［］.\\[\\]/~⁓∼～";

static NSString *INVALID_COUNTRY_CODE_STR = @"Invalid country calling code";
static NSString *NOT_A_NUMBER_STR = @"The string supplied did not seem to be a phone number";
static NSString *TOO_SHORT_AFTER_IDD_STR = @"Phone number too short after IDD";
static NSString *TOO_SHORT_NSN_STR = @"The string supplied is too short to be a phone number";
static NSString *TOO_LONG_STR = @"The string supplied is too long to be a phone number";

static NSString *UNKNOWN_REGION_ = @"ZZ";
static NSString *COLOMBIA_MOBILE_TO_FIXED_LINE_PREFIX = @"3";
static NSString *PLUS_SIGN = @"+";
static NSString *STAR_SIGN = @"*";
static NSString *RFC3966_EXTN_PREFIX = @";ext=";
static NSString *RFC3966_PREFIX = @"tel:";
static NSString *RFC3966_PHONE_CONTEXT = @";phone-context=";
static NSString *RFC3966_ISDN_SUBADDRESS = @";isub=";
static NSString *DEFAULT_EXTN_PREFIX = @" ext. ";
static NSString *VALID_ALPHA = @"A-Za-z";

#pragma mark - Static regular expression strings -
static NSString *NON_DIGITS_PATTERN = @"\\D+";
static NSString *CC_PATTERN = @"\\$CC";
static NSString *FIRST_GROUP_PATTERN = @"(\\$\\d)";
static NSString *FIRST_GROUP_ONLY_PREFIX_PATTERN = @"^\\(?\\$1\\)?";
static NSString *NP_PATTERN = @"\\$NP";
static NSString *FG_PATTERN = @"\\$FG";
static NSString *VALID_ALPHA_PHONE_PATTERN_STRING = @"(?:.*?[A-Za-z]){3}.*";

static NSString *UNIQUE_INTERNATIONAL_PREFIX = @"[\\d]+(?:[~\\u2053\\u223C\\uFF5E][\\d]+)?";

static NSString *LEADING_PLUS_CHARS_PATTERN;
static NSString *EXTN_PATTERN;
static NSString *SEPARATOR_PATTERN;
static NSString *VALID_PHONE_NUMBER_PATTERN;
static NSString *VALID_START_CHAR_PATTERN;
static NSString *UNWANTED_END_CHAR_PATTERN;
static NSString *SECOND_NUMBER_START_PATTERN;

static NSDictionary *ALPHA_MAPPINGS;
static NSDictionary *ALL_NORMALIZATION_MAPPINGS;
static NSDictionary *DIALLABLE_CHAR_MAPPINGS;
static NSDictionary *ALL_PLUS_NUMBER_GROUPING_SYMBOLS;

static NSRegularExpression *PLUS_CHARS_PATTERN;
static NSRegularExpression *CAPTURING_DIGIT_PATTERN;
static NSRegularExpression *VALID_ALPHA_PHONE_PATTERN;

static NSDictionary *DIGIT_MAPPINGS;


#pragma mark - Deprecated methods

+ (NBPhoneNumberUtil *)sharedInstance
{
    static NBPhoneNumberUtil *sharedOnceInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{ sharedOnceInstance = [[self alloc] init]; });
    return sharedOnceInstance;
}


#pragma mark - NSError

- (NSError*)errorWithObject:(id)obj withDomain:(NSString *)domain
{
    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:obj forKey:NSLocalizedDescriptionKey];
    NSError *error = [NSError errorWithDomain:domain code:0 userInfo:userInfo];
    return error;
}


- (NSRegularExpression *)entireRegularExpressionWithPattern:(NSString *)regexPattern
                                                    options:(NSRegularExpressionOptions)options
                                                      error:(NSError **)error
{
    [entireStringCacheLock lock];
    
    @try {
        if (! entireStringRegexCache) {
            entireStringRegexCache = [[NSMutableDictionary alloc] init];
        }
        
        NSRegularExpression *regex = [entireStringRegexCache objectForKey:regexPattern];
        if (! regex)
        {
            NSString *finalRegexString = regexPattern;
            if ([regexPattern rangeOfString:@"^"].location == NSNotFound) {
                finalRegexString = [NSString stringWithFormat:@"^(?:%@)$", regexPattern];
            }
            
            regex = [self regularExpressionWithPattern:finalRegexString options:0 error:error];
            [entireStringRegexCache setObject:regex forKey:regexPattern];
        }
        
        return regex;
    }
    @finally {
        [entireStringCacheLock unlock];
    }
}


- (NSRegularExpression *)regularExpressionWithPattern:(NSString *)pattern options:(NSRegularExpressionOptions)options error:(NSError **)error
{
    [lockPatternCache lock];
    
    @try {
        if (!regexPatternCache) {
            regexPatternCache = [[NSMutableDictionary alloc] init];
        }
        
        NSRegularExpression *regex = [regexPatternCache objectForKey:pattern];
        if (!regex) {
            regex = [NSRegularExpression regularExpressionWithPattern:pattern options:options error:error];
            [regexPatternCache setObject:regex forKey:pattern];
        }
        return regex;
    }
    @finally {
        [lockPatternCache unlock];
    }
}


- (NSMutableArray*)componentsSeparatedByRegex:(NSString *)sourceString regex:(NSString *)pattern
{
    NSString *replacedString = [self replaceStringByRegex:sourceString regex:pattern withTemplate:@"<SEP>"];
    NSMutableArray *resArray = [[replacedString componentsSeparatedByString:@"<SEP>"] mutableCopy];
    [resArray removeObject:@""];
    return resArray;
}


- (int)stringPositionByRegex:(NSString *)sourceString regex:(NSString *)pattern
{
    if (sourceString == nil || sourceString.length <= 0 || pattern == nil || pattern.length <= 0) {
        return -1;
    }
    
    NSError *error = nil;
    NSRegularExpression *currentPattern = [self regularExpressionWithPattern:pattern options:0 error:&error];
    NSArray *matches = [currentPattern matchesInString:sourceString options:0 range:NSMakeRange(0, sourceString.length)];
    
    int foundPosition = -1;
    
    if (matches.count > 0) {
        NSTextCheckingResult *match = [matches objectAtIndex:0];
        return (int)match.range.location;
    }
    
    return foundPosition;
}


- (int)indexOfStringByString:(NSString *)sourceString target:(NSString *)targetString
{
    NSRange finded = [sourceString rangeOfString:targetString];
    if (finded.location != NSNotFound) {
        return (int)finded.location;
    }
    
    return -1;
}


- (NSString *)replaceFirstStringByRegex:(NSString *)sourceString regex:(NSString *)pattern withTemplate:(NSString *)templateString
{
    NSString *replacementResult = [sourceString copy];
    NSError *error = nil;
    
    NSRegularExpression *currentPattern = [self regularExpressionWithPattern:pattern options:0 error:&error];
    NSRange replaceRange = [currentPattern rangeOfFirstMatchInString:sourceString options:0 range:NSMakeRange(0, sourceString.length)];
    
    if (replaceRange.location != NSNotFound) {
        replacementResult = [currentPattern stringByReplacingMatchesInString:[sourceString mutableCopy] options:0
                                                                       range:replaceRange
                                                                withTemplate:templateString];
    }
    
    return replacementResult;
}


- (NSString *)replaceStringByRegex:(NSString *)sourceString regex:(NSString *)pattern withTemplate:(NSString *)templateString
{
    NSString *replacementResult = [sourceString copy];
    NSError *error = nil;
    
    NSRegularExpression *currentPattern = [self regularExpressionWithPattern:pattern options:0 error:&error];
    NSArray *matches = [currentPattern matchesInString:sourceString options:0 range:NSMakeRange(0, sourceString.length)];
    
    if ([matches count] == 1) {
        NSRange replaceRange = [currentPattern rangeOfFirstMatchInString:sourceString options:0 range:NSMakeRange(0, sourceString.length)];
        
        if (replaceRange.location != NSNotFound) {
            replacementResult = [currentPattern stringByReplacingMatchesInString:[sourceString mutableCopy] options:0
                                                                           range:replaceRange
                                                                    withTemplate:templateString];
        }
        return replacementResult;
    }
    
    if ([matches count] > 1) {
        replacementResult = [currentPattern stringByReplacingMatchesInString:[replacementResult mutableCopy] options:0
                                                                       range:NSMakeRange(0, sourceString.length) withTemplate:templateString];
        return replacementResult;
    }
    
    return replacementResult;
}


- (NSTextCheckingResult*)matcheFirstByRegex:(NSString *)sourceString regex:(NSString *)pattern
{
    NSError *error = nil;
    NSRegularExpression *currentPattern = [self regularExpressionWithPattern:pattern options:0 error:&error];
    NSArray *matches = [currentPattern matchesInString:sourceString options:0 range:NSMakeRange(0, sourceString.length)];
    if ([matches count] > 0)
        return [matches objectAtIndex:0];
    return nil;
}


- (NSArray*)matchesByRegex:(NSString *)sourceString regex:(NSString *)pattern
{
    NSError *error = nil;
    NSRegularExpression *currentPattern = [self regularExpressionWithPattern:pattern options:0 error:&error];
    NSArray *matches = [currentPattern matchesInString:sourceString options:0 range:NSMakeRange(0, sourceString.length)];
    return matches;
}


- (NSArray*)matchedStringByRegex:(NSString *)sourceString regex:(NSString *)pattern
{
    NSArray *matches = [self matchesByRegex:sourceString regex:pattern];
    NSMutableArray *matchString = [[NSMutableArray alloc] init];
    
    for (NSTextCheckingResult *match in matches) {
        NSString *curString = [sourceString substringWithRange:match.range];
        [matchString addObject:curString];
    }
    
    return matchString;
}


- (BOOL)isStartingStringByRegex:(NSString *)sourceString regex:(NSString *)pattern
{
    NSError *error = nil;
    NSRegularExpression *currentPattern = [self regularExpressionWithPattern:pattern options:0 error:&error];
    NSArray *matches = [currentPattern matchesInString:sourceString options:0 range:NSMakeRange(0, sourceString.length)];
    
    for (NSTextCheckingResult *match in matches) {
        if (match.range.location == 0) {
            return YES;
        }
    }
    
    return NO;
}


- (NSString *)stringByReplacingOccurrencesString:(NSString *)sourceString withMap:(NSDictionary *)dicMap removeNonMatches:(BOOL)bRemove
{
    NSMutableString *targetString = [[NSMutableString alloc] initWithString:@""];
    
    for(unsigned int i=0; i<sourceString.length; i++)
    {
        unichar oneChar = [sourceString characterAtIndex:i];
        NSString *keyString = [NSString stringWithCharacters:&oneChar length:1];
        NSString *mappedValue = [dicMap objectForKey:keyString];
        if (mappedValue != nil) {
            [targetString appendString:mappedValue];
        } else {
            if (bRemove == NO) {
                [targetString appendString:keyString];
            }
        }
    }
    
    return targetString;
}


- (BOOL)isAllDigits:(NSString *)sourceString
{
    NSCharacterSet *nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
    NSRange r = [sourceString rangeOfCharacterFromSet:nonNumbers];
    return r.location == NSNotFound;
}


- (BOOL)isNumeric:(NSString *)sourceString
{
    NSScanner *sc = [NSScanner scannerWithString:sourceString];
    if ([sc scanFloat:NULL]) {
        return [sc isAtEnd];
    }
    return NO;
}


- (BOOL)isNaN:(NSString *)sourceString
{
    if ([self isNumeric:sourceString]) return NO;
    return YES;
}


- (NSString *)getNationalSignificantNumber:(NBPhoneNumber *)phoneNumber
{
    if (phoneNumber.italianLeadingZero) {
        return [NSString stringWithFormat:@"0%@", phoneNumber.nationalNumber];
    }
    
    return [phoneNumber.nationalNumber stringValue];
}


#pragma mark - Initializations -

- (id)init
{
    self = [super init];
    if (self)
    {
        lockPatternCache = [[NSLock alloc] init];
        entireStringCacheLock = [[NSLock alloc] init];
        [self initRegularExpressionSet];
        [self initNormalizationMappings];
    }
    
    return self;
}


- (void)initRegularExpressionSet
{
    NSString *EXTN_PATTERNS_FOR_PARSING = @"(?:;ext=([0-9０-９٠-٩۰-۹]{1,7})|[  \\t,]*(?:e?xt(?:ensi(?:ó?|ó))?n?|ｅ?ｘｔｎ?|[,xｘX#＃~～]|int|anexo|ｉｎｔ)[:\\.．]?[  \\t,-]*([0-9０-９٠-٩۰-۹]{1,7})#?|[- ]+([0-9０-９٠-٩۰-۹]{1,5})#)$";
    
    NSError *error = nil;
    
    if (!PLUS_CHARS_PATTERN) {
        PLUS_CHARS_PATTERN = [self regularExpressionWithPattern:[NSString stringWithFormat:@"[%@]+", NB_PLUS_CHARS] options:0 error:&error];
    }
    
    if (!LEADING_PLUS_CHARS_PATTERN) {
        LEADING_PLUS_CHARS_PATTERN = [NSString stringWithFormat:@"^[%@]+", NB_PLUS_CHARS];
    }
    
    if (!CAPTURING_DIGIT_PATTERN) {
        CAPTURING_DIGIT_PATTERN = [self regularExpressionWithPattern:[NSString stringWithFormat:@"([%@])", NB_VALID_DIGITS_STRING] options:0 error:&error];
    }
    
    if (!VALID_START_CHAR_PATTERN) {
        VALID_START_CHAR_PATTERN = [NSString stringWithFormat:@"[%@%@]", NB_PLUS_CHARS, NB_VALID_DIGITS_STRING];
    }
    
    if (!SECOND_NUMBER_START_PATTERN) {
        SECOND_NUMBER_START_PATTERN = @"[\\\\\\/] *x";
    }
    
    if (!VALID_ALPHA_PHONE_PATTERN) {
        VALID_ALPHA_PHONE_PATTERN = [self regularExpressionWithPattern:VALID_ALPHA_PHONE_PATTERN_STRING options:0 error:&error];
    }
    
    if (!UNWANTED_END_CHAR_PATTERN) {
        UNWANTED_END_CHAR_PATTERN = [NSString stringWithFormat:@"[^%@%@#]+$", NB_VALID_DIGITS_STRING, VALID_ALPHA];
    }
    
    if (!EXTN_PATTERN) {
        EXTN_PATTERN = [NSString stringWithFormat:@"(?:%@)$", EXTN_PATTERNS_FOR_PARSING];
    }
    
    if (!SEPARATOR_PATTERN) {
        SEPARATOR_PATTERN = [NSString stringWithFormat:@"[%@]+", VALID_PUNCTUATION];
    }
    
    if (!VALID_PHONE_NUMBER_PATTERN) {
        VALID_PHONE_NUMBER_PATTERN = @"^[0-9０-９٠-٩۰-۹]{2}$|^[+＋]*(?:[-x‐-―−ー－-／  ­​⁠　()（）［］.\\[\\]/~⁓∼～*]*[0-9０-９٠-٩۰-۹]){3,}[-x‐-―−ー－-／  ­​⁠　()（）［］.\\[\\]/~⁓∼～*A-Za-z0-9０-９٠-٩۰-۹]*(?:;ext=([0-9０-９٠-٩۰-۹]{1,7})|[  \\t,]*(?:e?xt(?:ensi(?:ó?|ó))?n?|ｅ?ｘｔｎ?|[,xｘ#＃~～]|int|anexo|ｉｎｔ)[:\\.．]?[  \\t,-]*([0-9０-９٠-٩۰-۹]{1,7})#?|[- ]+([0-9０-９٠-٩۰-۹]{1,5})#)?$";
    }
}

- (NSDictionary *)DIGIT_MAPPINGS
{
    if (!DIGIT_MAPPINGS) {
        DIGIT_MAPPINGS = [NSDictionary dictionaryWithObjectsAndKeys:
                          @"0", @"0", @"1", @"1", @"2", @"2", @"3", @"3", @"4", @"4", @"5", @"5", @"6", @"6", @"7", @"7", @"8", @"8", @"9", @"9",
                          // Fullwidth digit 0 to 9
                          @"0", @"\uFF10", @"1", @"\uFF11", @"2", @"\uFF12", @"3", @"\uFF13", @"4", @"\uFF14", @"5", @"\uFF15", @"6", @"\uFF16", @"7", @"\uFF17", @"8", @"\uFF18", @"9", @"\uFF19",
                          // Arabic-indic digit 0 to 9
                          @"0", @"\u0660", @"1", @"\u0661", @"2", @"\u0662", @"3", @"\u0663", @"4", @"\u0664", @"5", @"\u0665", @"6", @"\u0666", @"7", @"\u0667", @"8", @"\u0668", @"9", @"\u0669",
                          // Eastern-Arabic digit 0 to 9
                          @"0", @"\u06F0", @"1", @"\u06F1",  @"2", @"\u06F2", @"3", @"\u06F3", @"4", @"\u06F4", @"5", @"\u06F5", @"6", @"\u06F6", @"7", @"\u06F7", @"8", @"\u06F8", @"9", @"\u06F9", nil];
    }
    return DIGIT_MAPPINGS;
}


- (void)initNormalizationMappings
{
    if (!DIALLABLE_CHAR_MAPPINGS) {
        DIALLABLE_CHAR_MAPPINGS = [NSDictionary dictionaryWithObjectsAndKeys:
                                   @"0", @"0", @"1", @"1", @"2", @"2", @"3", @"3", @"4", @"4", @"5", @"5", @"6", @"6", @"7", @"7", @"8", @"8", @"9", @"9",
                                   @"+", @"+", @"*", @"*", nil];
    }
    
    if (!ALPHA_MAPPINGS) {
        ALPHA_MAPPINGS = [NSDictionary dictionaryWithObjectsAndKeys:
                          @"2", @"A", @"2", @"B", @"2", @"C", @"3", @"D", @"3", @"E", @"3", @"F", @"4", @"G", @"4", @"H", @"4", @"I", @"5", @"J",
                          @"5", @"K", @"5", @"L", @"6", @"M", @"6", @"N", @"6", @"O", @"7", @"P", @"7", @"Q", @"7", @"R", @"7", @"S", @"8", @"T",
                          @"8", @"U", @"8", @"V", @"9", @"W", @"9", @"X", @"9", @"Y", @"9", @"Z", nil];
    }
    
    if (!ALL_NORMALIZATION_MAPPINGS) {
        ALL_NORMALIZATION_MAPPINGS = [NSDictionary dictionaryWithObjectsAndKeys:
                                      @"0", @"0", @"1", @"1", @"2", @"2", @"3", @"3", @"4", @"4", @"5", @"5", @"6", @"6", @"7", @"7", @"8", @"8", @"9", @"9",
                                      // Fullwidth digit 0 to 9
                                      @"0", @"\uFF10", @"1", @"\uFF11", @"2", @"\uFF12", @"3", @"\uFF13", @"4", @"\uFF14", @"5", @"\uFF15", @"6", @"\uFF16", @"7", @"\uFF17", @"8", @"\uFF18", @"9", @"\uFF19",
                                      // Arabic-indic digit 0 to 9
                                      @"0", @"\u0660", @"1", @"\u0661", @"2", @"\u0662", @"3", @"\u0663", @"4", @"\u0664", @"5", @"\u0665", @"6", @"\u0666", @"7", @"\u0667", @"8", @"\u0668", @"9", @"\u0669",
                                      // Eastern-Arabic digit 0 to 9
                                      @"0", @"\u06F0", @"1", @"\u06F1",  @"2", @"\u06F2", @"3", @"\u06F3", @"4", @"\u06F4", @"5", @"\u06F5", @"6", @"\u06F6", @"7", @"\u06F7", @"8", @"\u06F8", @"9", @"\u06F9",
                                      @"2", @"A", @"2", @"B", @"2", @"C", @"3", @"D", @"3", @"E", @"3", @"F", @"4", @"G", @"4", @"H", @"4", @"I", @"5", @"J",
                                      @"5", @"K", @"5", @"L", @"6", @"M", @"6", @"N", @"6", @"O", @"7", @"P", @"7", @"Q", @"7", @"R", @"7", @"S", @"8", @"T",
                                      @"8", @"U", @"8", @"V", @"9", @"W", @"9", @"X", @"9", @"Y", @"9", @"Z", nil];
    }
    
    if (!ALL_PLUS_NUMBER_GROUPING_SYMBOLS) {
        ALL_PLUS_NUMBER_GROUPING_SYMBOLS = [NSDictionary dictionaryWithObjectsAndKeys:
                                            @"0", @"0", @"1", @"1", @"2", @"2", @"3", @"3", @"4", @"4", @"5", @"5", @"6", @"6", @"7", @"7", @"8", @"8", @"9", @"9",
                                            @"A", @"A", @"B", @"B", @"C", @"C", @"D", @"D", @"E", @"E", @"F", @"F", @"G", @"G", @"H", @"H", @"I", @"I", @"J", @"J",
                                            @"K", @"K", @"L", @"L", @"M", @"M", @"N", @"N", @"O", @"O", @"P", @"P", @"Q", @"Q", @"R", @"R", @"S", @"S", @"T", @"T",
                                            @"U", @"U", @"V", @"V", @"W", @"W", @"X", @"X", @"Y", @"Y", @"Z", @"Z", @"A", @"a", @"B", @"b", @"C", @"c", @"D", @"d",
                                            @"E", @"e", @"F", @"f", @"G", @"g", @"H", @"h", @"I", @"i", @"J", @"j", @"K", @"k", @"L", @"l", @"M", @"m", @"N", @"n",
                                            @"O", @"o", @"P", @"p", @"Q", @"q", @"R", @"r", @"S", @"s", @"T", @"t", @"U", @"u", @"V", @"v", @"W", @"w", @"X", @"x",
                                            @"Y", @"y", @"Z", @"z", @"-", @"-", @"-", @"\uFF0D", @"-", @"\u2010", @"-", @"\u2011", @"-", @"\u2012", @"-", @"\u2013", @"-", @"\u2014", @"-", @"\u2015",
                                            @"-", @"\u2212", @"/", @"/", @"/", @"\uFF0F", @" ", @" ", @" ", @"\u3000", @" ", @"\u2060", @".", @".", @".", @"\uFF0E", nil];
    }
}





#pragma mark - Metadata manager (phonenumberutil.js) functions -
/**
 * Attempts to extract a possible number from the string passed in. This
 * currently strips all leading characters that cannot be used to start a phone
 * number. Characters that can be used to start a phone number are defined in
 * the VALID_START_CHAR_PATTERN. If none of these characters are found in the
 * number passed in, an empty string is returned. This function also attempts to
 * strip off any alternative extensions or endings if two or more are present,
 * such as in the case of: (530) 583-6985 x302/x2303. The second extension here
 * makes this actually two phone numbers, (530) 583-6985 x302 and (530) 583-6985
 * x2303. We remove the second extension so that the first number is parsed
 * correctly.
 *
 * @param {string} number the string that might contain a phone number.
 * @return {string} the number, stripped of any non-phone-number prefix (such as
 *     'Tel:') or an empty string if no character used to start phone numbers
 *     (such as + or any digit) is found in the number.
 */
- (NSString *)extractPossibleNumber:(NSString *)number
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    
    number = [helper normalizeNonBreakingSpace:number];
    
    NSString *possibleNumber = @"";
    int start = [self stringPositionByRegex:number regex:VALID_START_CHAR_PATTERN];
    
    if (start >= 0)
    {
        possibleNumber = [number substringFromIndex:start];
        // Remove trailing non-alpha non-numerical characters.
        possibleNumber = [self replaceStringByRegex:possibleNumber regex:UNWANTED_END_CHAR_PATTERN withTemplate:@""];
        
        // Check for extra numbers at the end.
        int secondNumberStart = [self stringPositionByRegex:number regex:SECOND_NUMBER_START_PATTERN];
        if (secondNumberStart > 0)
        {
            possibleNumber = [possibleNumber substringWithRange:NSMakeRange(0, secondNumberStart - 1)];
        }
    }
    else
    {
        possibleNumber = @"";
    }
    
    return possibleNumber;
}


/**
 * Checks to see if the string of characters could possibly be a phone number at
 * all. At the moment, checks to see that the string begins with at least 2
 * digits, ignoring any punctuation commonly found in phone numbers. This method
 * does not require the number to be normalized in advance - but does assume
 * that leading non-number symbols have been removed, such as by the method
 * extractPossibleNumber.
 *
 * @param {string} number string to be checked for viability as a phone number.
 * @return {boolean} NO if the number could be a phone number of some sort,
 *     otherwise NO.
 */
- (BOOL)isViablePhoneNumber:(NSString *)phoneNumber
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    phoneNumber = [helper normalizeNonBreakingSpace:phoneNumber];
    
    if (phoneNumber.length < MIN_LENGTH_FOR_NSN_)
    {
        return NO;
    }
    
    return [self matchesEntirely:VALID_PHONE_NUMBER_PATTERN string:phoneNumber];
}


/**
 * Normalizes a string of characters representing a phone number. This performs
 * the following conversions:
 *   Punctuation is stripped.
 *   For ALPHA/VANITY numbers:
 *   Letters are converted to their numeric representation on a telephone
 *       keypad. The keypad used here is the one defined in ITU Recommendation
 *       E.161. This is only done if there are 3 or more letters in the number,
 *       to lessen the risk that such letters are typos.
 *   For other numbers:
 *   Wide-ascii digits are converted to normal ASCII (European) digits.
 *   Arabic-Indic numerals are converted to European numerals.
 *   Spurious alpha characters are stripped.
 *
 * @param {string} number a string of characters representing a phone number.
 * @return {string} the normalized string version of the phone number.
 */
- (NSString *)normalizePhoneNumber:(NSString *)number
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    number = [helper normalizeNonBreakingSpace:number];
    
    if ([self matchesEntirely:VALID_ALPHA_PHONE_PATTERN_STRING string:number])
    {
        return [self normalizeHelper:number normalizationReplacements:ALL_NORMALIZATION_MAPPINGS removeNonMatches:true];
    }
    else
    {
        return [self normalizeDigitsOnly:number];
    }
    
    return nil;
}


/**
 * Normalizes a string of characters representing a phone number. This is a
 * wrapper for normalize(String number) but does in-place normalization of the
 * StringBuffer provided.
 *
 * @param {!goog.string.StringBuffer} number a StringBuffer of characters
 *     representing a phone number that will be normalized in place.
 * @private
 */

- (void)normalizeSB:(NSString **)number
{
    if (number == NULL) {
        return;
    }
    
    (*number) = [self normalizePhoneNumber:(*number)];
}


/**
 * Normalizes a string of characters representing a phone number. This converts
 * wide-ascii and arabic-indic numerals to European numerals, and strips
 * punctuation and alpha characters.
 *
 * @param {string} number a string of characters representing a phone number.
 * @return {string} the normalized string version of the phone number.
 */
- (NSString *)normalizeDigitsOnly:(NSString *)number
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    number = [helper normalizeNonBreakingSpace:number];
    
    return [self stringByReplacingOccurrencesString:number withMap:self.DIGIT_MAPPINGS removeNonMatches:YES];
}


/**
 * Converts all alpha characters in a number to their respective digits on a
 * keypad, but retains existing formatting. Also converts wide-ascii digits to
 * normal ascii digits, and converts Arabic-Indic numerals to European numerals.
 *
 * @param {string} number a string of characters representing a phone number.
 * @return {string} the normalized string version of the phone number.
 */
- (NSString *)convertAlphaCharactersInNumber:(NSString *)number
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    number = [helper normalizeNonBreakingSpace:number];
    return [self stringByReplacingOccurrencesString:number withMap:ALL_NORMALIZATION_MAPPINGS removeNonMatches:NO];
}


/**
 * Gets the length of the geographical area code from the
 * {@code national_number} field of the PhoneNumber object passed in, so that
 * clients could use it to split a national significant number into geographical
 * area code and subscriber number. It works in such a way that the resultant
 * subscriber number should be diallable, at least on some devices. An example
 * of how this could be used:
 *
 * <pre>
 * var phoneUtil = getInstance();
 * var number = phoneUtil.parse('16502530000', 'US');
 * var nationalSignificantNumber =
 *     phoneUtil.getNationalSignificantNumber(number);
 * var areaCode;
 * var subscriberNumber;
 *
 * var areaCodeLength = phoneUtil.getLengthOfGeographicalAreaCode(number);
 * if (areaCodeLength > 0) {
 *   areaCode = nationalSignificantNumber.substring(0, areaCodeLength);
 *   subscriberNumber = nationalSignificantNumber.substring(areaCodeLength);
 * } else {
 *   areaCode = '';
 *   subscriberNumber = nationalSignificantNumber;
 * }
 * </pre>
 *
 * N.B.: area code is a very ambiguous concept, so the I18N team generally
 * recommends against using it for most purposes, but recommends using the more
 * general {@code national_number} instead. Read the following carefully before
 * deciding to use this method:
 * <ul>
 *  <li> geographical area codes change over time, and this method honors those
 *    changes; therefore, it doesn't guarantee the stability of the result it
 *    produces.
 *  <li> subscriber numbers may not be diallable from all devices (notably
 *    mobile devices, which typically requires the full national_number to be
 *    dialled in most regions).
 *  <li> most non-geographical numbers have no area codes, including numbers
 *    from non-geographical entities.
 *  <li> some geographical numbers have no area codes.
 * </ul>
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the PhoneNumber object for
 *     which clients want to know the length of the area code.
 * @return {number} the length of area code of the PhoneNumber object passed in.
 */
- (int)getLengthOfGeographicalAreaCode:(NBPhoneNumber*)phoneNumber error:(NSError **)error
{
    int res = 0;
    @try {
        res = [self getLengthOfGeographicalAreaCode:phoneNumber];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL) {
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
        }
    }
    return res;
}


- (int)getLengthOfGeographicalAreaCode:(NBPhoneNumber*)phoneNumber
{
    NSString *regionCode = [self getRegionCodeForNumber:phoneNumber];
    
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NBPhoneMetaData *metadata = [helper getMetadataForRegion:regionCode];
    
    if (metadata == nil) {
        return 0;
    }
    // If a country doesn't use a national prefix, and this number doesn't have
    // an Italian leading zero, we assume it is a closed dialling plan with no
    // area codes.
    if (metadata.nationalPrefix == nil && phoneNumber.italianLeadingZero == NO) {
        return 0;
    }
    
    if ([self isNumberGeographical:phoneNumber] == NO) {
        return 0;
    }
    
    return [self getLengthOfNationalDestinationCode:phoneNumber];
}


/**
 * Gets the length of the national destination code (NDC) from the PhoneNumber
 * object passed in, so that clients could use it to split a national
 * significant number into NDC and subscriber number. The NDC of a phone number
 * is normally the first group of digit(s) right after the country calling code
 * when the number is formatted in the international format, if there is a
 * subscriber number part that follows. An example of how this could be used:
 *
 * <pre>
 * var phoneUtil = getInstance();
 * var number = phoneUtil.parse('18002530000', 'US');
 * var nationalSignificantNumber =
 *     phoneUtil.getNationalSignificantNumber(number);
 * var nationalDestinationCode;
 * var subscriberNumber;
 *
 * var nationalDestinationCodeLength =
 *     phoneUtil.getLengthOfNationalDestinationCode(number);
 * if (nationalDestinationCodeLength > 0) {
 *   nationalDestinationCode =
 *       nationalSignificantNumber.substring(0, nationalDestinationCodeLength);
 *   subscriberNumber =
 *       nationalSignificantNumber.substring(nationalDestinationCodeLength);
 * } else {
 *   nationalDestinationCode = '';
 *   subscriberNumber = nationalSignificantNumber;
 * }
 * </pre>
 *
 * Refer to the unittests to see the difference between this function and
 * {@link #getLengthOfGeographicalAreaCode}.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the PhoneNumber object for
 *     which clients want to know the length of the NDC.
 * @return {number} the length of NDC of the PhoneNumber object passed in.
 */
- (int)getLengthOfNationalDestinationCode:(NBPhoneNumber*)phoneNumber error:(NSError **)error
{
    int res = 0;
    
    @try {
        res = [self getLengthOfNationalDestinationCode:phoneNumber];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL) {
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
        }
    }
    
    return res;
}


- (int)getLengthOfNationalDestinationCode:(NBPhoneNumber*)phoneNumber
{
    NBPhoneNumber *copiedProto = nil;
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    
    if ([NBMetadataHelper hasValue:phoneNumber.extension]) {
        copiedProto = [phoneNumber copy];
        copiedProto.extension = nil;
    } else {
        copiedProto = phoneNumber;
    }
    
    NSString *nationalSignificantNumber = [self format:copiedProto numberFormat:NBEPhoneNumberFormatINTERNATIONAL];
    NSMutableArray *numberGroups = [[self componentsSeparatedByRegex:nationalSignificantNumber regex:NON_DIGITS_PATTERN] mutableCopy];
    
    // The pattern will start with '+COUNTRY_CODE ' so the first group will always
    // be the empty string (before the + symbol) and the second group will be the
    // country calling code. The third group will be area code if it is not the
    // last group.
    // NOTE: On IE the first group that is supposed to be the empty string does
    // not appear in the array of number groups... so make the result on non-IE
    // browsers to be that of IE.
    if ([numberGroups count] > 0 && ((NSString *)[numberGroups objectAtIndex:0]).length <= 0) {
        [numberGroups removeObjectAtIndex:0];
    }
    
    if ([numberGroups count] <= 2) {
        return 0;
    }
    
    NSArray *regionCodes = [helper regionCodeFromCountryCode:phoneNumber.countryCode];
    BOOL isExists = NO;
    
    for (NSString *regCode in regionCodes)
    {
        if ([regCode isEqualToString:@"AR"])
        {
            isExists = YES;
            break;
        }
    }
    
    if (isExists && [self getNumberType:phoneNumber] == NBEPhoneNumberTypeMOBILE)
    {
        // Argentinian mobile numbers, when formatted in the international format,
        // are in the form of +54 9 NDC XXXX.... As a result, we take the length of
        // the third group (NDC) and add 1 for the digit 9, which also forms part of
        // the national significant number.
        //
        // TODO: Investigate the possibility of better modeling the metadata to make
        // it easier to obtain the NDC.
        return (int)((NSString *)[numberGroups objectAtIndex:2]).length + 1;
    }
    
    return (int)((NSString *)[numberGroups objectAtIndex:1]).length;
}


/**
 * Normalizes a string of characters representing a phone number by replacing
 * all characters found in the accompanying map with the values therein, and
 * stripping all other characters if removeNonMatches is NO.
 *
 * @param {string} number a string of characters representing a phone number.
 * @param {!Object.<string, string>} normalizationReplacements a mapping of
 *     characters to what they should be replaced by in the normalized version
 *     of the phone number.
 * @param {boolean} removeNonMatches indicates whether characters that are not
 *     able to be replaced should be stripped from the number. If this is NO,
 *     they will be left unchanged in the number.
 * @return {string} the normalized string version of the phone number.
 * @private
 */
- (NSString *)normalizeHelper:(NSString *)sourceString normalizationReplacements:(NSDictionary*)normalizationReplacements
            removeNonMatches:(BOOL)removeNonMatches
{
    NSMutableString *normalizedNumber = [[NSMutableString alloc] init];
    unichar character = 0;
    NSString *newDigit = @"";
    unsigned int numberLength = (unsigned int)sourceString.length;
    
    for (unsigned int i = 0; i<numberLength; ++i)
    {
        character = [sourceString characterAtIndex:i];
        newDigit = [normalizationReplacements objectForKey:[[NSString stringWithFormat: @"%C", character] uppercaseString]];
        if (newDigit != nil)
        {
            [normalizedNumber appendString:newDigit];
        }
        else if (removeNonMatches == NO)
        {
            [normalizedNumber appendString:[NSString stringWithFormat: @"%C", character]];
        }
        // If neither of the above are NO, we remove this character.
        
        //NSLog(@"[%@]", normalizedNumber);
    }
    
    return normalizedNumber;
}


/**
 * Helper function to check if the national prefix formatting rule has the first
 * group only, i.e., does not start with the national prefix.
 *
 * @param {string} nationalPrefixFormattingRule The formatting rule for the
 *     national prefix.
 * @return {boolean} NO if the national prefix formatting rule has the first
 *     group only.
 */
- (BOOL)formattingRuleHasFirstGroupOnly:(NSString *)nationalPrefixFormattingRule
{
    BOOL hasFound = NO;
    if ([self stringPositionByRegex:nationalPrefixFormattingRule regex:FIRST_GROUP_ONLY_PREFIX_PATTERN] >= 0)
    {
        hasFound = YES;
    }
    
    return (([nationalPrefixFormattingRule length] == 0) || hasFound);
}


/**
 * Tests whether a phone number has a geographical association. It checks if
 * the number is associated to a certain region in the country where it belongs
 * to. Note that this doesn't verify if the number is actually in use.
 *
 * @param {i18n.phonenumbers.PhoneNumber} phoneNumber The phone number to test.
 * @return {boolean} NO if the phone number has a geographical association.
 * @private
 */
- (BOOL)isNumberGeographical:(NBPhoneNumber*)phoneNumber
{
    NBEPhoneNumberType numberType = [self getNumberType:phoneNumber];
    // TODO: Include mobile phone numbers from countries like Indonesia, which
    // has some mobile numbers that are geographical.
    return numberType == NBEPhoneNumberTypeFIXED_LINE || numberType == NBEPhoneNumberTypeFIXED_LINE_OR_MOBILE;
}


/**
 * Helper function to check region code is not unknown or nil.
 *
 * @param {?string} regionCode the ISO 3166-1 two-letter region code.
 * @return {boolean} NO if region code is valid.
 * @private
 */
- (BOOL)isValidRegionCode:(NSString *)regionCode
{
    // In Java we check whether the regionCode is contained in supportedRegions
    // that is built out of all the values of countryCallingCodeToRegionCodeMap
    // (countryCodeToRegionCodeMap in JS) minus REGION_CODE_FOR_NON_GEO_ENTITY.
    // In JS we check whether the regionCode is contained in the keys of
    // countryToMetadata but since for non-geographical country calling codes
    // (e.g. +800) we use the country calling codes instead of the region code as
    // key in the map we have to make sure regionCode is not a number to prevent
    // returning NO for non-geographical country calling codes.
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    return [NBMetadataHelper hasValue:regionCode] && [self isNaN:regionCode] && [helper getMetadataForRegion:regionCode.uppercaseString] != nil;
}


/**
 * Helper function to check the country calling code is valid.
 *
 * @param {number} countryCallingCode the country calling code.
 * @return {boolean} NO if country calling code code is valid.
 * @private
 */
- (BOOL)hasValidCountryCallingCode:(NSNumber*)countryCallingCode
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    id res = [helper regionCodeFromCountryCode:countryCallingCode];
    if (res != nil) {
        return YES;
    }
    
    return NO;
}


/**
 * Formats a phone number in the specified format using default rules. Note that
 * this does not promise to produce a phone number that the user can dial from
 * where they are - although we do format in either 'national' or
 * 'international' format depending on what the client asks for, we do not
 * currently support a more abbreviated format, such as for users in the same
 * 'area' who could potentially dial the number without area code. Note that if
 * the phone number has a country calling code of 0 or an otherwise invalid
 * country calling code, we cannot work out which formatting rules to apply so
 * we return the national significant number with no formatting applied.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be
 *     formatted.
 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
 *     phone number should be formatted into.
 * @return {string} the formatted phone number.
 */
- (NSString *)format:(NBPhoneNumber*)phoneNumber numberFormat:(NBEPhoneNumberFormat)numberFormat error:(NSError**)error
{
    NSString *res = nil;
    @try {
        res = [self format:phoneNumber numberFormat:numberFormat];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    return res;
}

- (NSString *)format:(NBPhoneNumber*)phoneNumber numberFormat:(NBEPhoneNumberFormat)numberFormat
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    
    if ([phoneNumber.nationalNumber isEqualToNumber:@0] && [NBMetadataHelper hasValue:phoneNumber.rawInput]) {
        // Unparseable numbers that kept their raw input just use that.
        // This is the only case where a number can be formatted as E164 without a
        // leading '+' symbol (but the original number wasn't parseable anyway).
        // TODO: Consider removing the 'if' above so that unparseable strings
        // without raw input format to the empty string instead of "+00"
        /** @type {string} */
        NSString *rawInput = phoneNumber.rawInput;
        if ([NBMetadataHelper hasValue:rawInput]) {
            return rawInput;
        }
    }
    
    NSNumber *countryCallingCode = phoneNumber.countryCode;
    NSString *nationalSignificantNumber = [self getNationalSignificantNumber:phoneNumber];
    
    if (numberFormat == NBEPhoneNumberFormatE164)
    {
        // Early exit for E164 case (even if the country calling code is invalid)
        // since no formatting of the national number needs to be applied.
        // Extensions are not formatted.
        return [self prefixNumberWithCountryCallingCode:countryCallingCode phoneNumberFormat:NBEPhoneNumberFormatE164
                                formattedNationalNumber:nationalSignificantNumber formattedExtension:@""];
    }
    
    if ([self hasValidCountryCallingCode:countryCallingCode] == NO)
    {
        return nationalSignificantNumber;
    }
    
    // Note getRegionCodeForCountryCode() is used because formatting information
    // for regions which share a country calling code is contained by only one
    // region for performance reasons. For example, for NANPA regions it will be
    // contained in the metadata for US.
    NSArray *regionCodeArray = [helper regionCodeFromCountryCode:countryCallingCode];
    NSString *regionCode = [regionCodeArray objectAtIndex:0];
    
    // Metadata cannot be nil because the country calling code is valid (which
    // means that the region code cannot be ZZ and must be one of our supported
    // region codes).
    NBPhoneMetaData *metadata = [self getMetadataForRegionOrCallingCode:countryCallingCode regionCode:regionCode];
    NSString *formattedExtension = [self maybeGetFormattedExtension:phoneNumber metadata:metadata numberFormat:numberFormat];
    NSString *formattedNationalNumber = [self formatNsn:nationalSignificantNumber metadata:metadata phoneNumberFormat:numberFormat carrierCode:nil];
    
    return [self prefixNumberWithCountryCallingCode:countryCallingCode phoneNumberFormat:numberFormat
                            formattedNationalNumber:formattedNationalNumber formattedExtension:formattedExtension];
}


/**
 * Formats a phone number in the specified format using client-defined
 * formatting rules. Note that if the phone number has a country calling code of
 * zero or an otherwise invalid country calling code, we cannot work out things
 * like whether there should be a national prefix applied, or how to format
 * extensions, so we return the national significant number with no formatting
 * applied.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone  number to be
 *     formatted.
 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
 *     phone number should be formatted into.
 * @param {Array.<i18n.phonenumbers.NumberFormat>} userDefinedFormats formatting
 *     rules specified by clients.
 * @return {string} the formatted phone number.
 */
- (NSString *)formatByPattern:(NBPhoneNumber*)number numberFormat:(NBEPhoneNumberFormat)numberFormat userDefinedFormats:(NSArray*)userDefinedFormats error:(NSError**)error
{
    NSString *res = nil;
    @try {
        res = [self formatByPattern:number numberFormat:numberFormat userDefinedFormats:userDefinedFormats];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    return res;
}


- (NSString *)formatByPattern:(NBPhoneNumber*)number numberFormat:(NBEPhoneNumberFormat)numberFormat userDefinedFormats:(NSArray*)userDefinedFormats
{
    NSNumber *countryCallingCode = number.countryCode;
    NSString *nationalSignificantNumber = [self getNationalSignificantNumber:number];
    
    if ([self hasValidCountryCallingCode:countryCallingCode] == NO) {
        return nationalSignificantNumber;
    }
    
    // Note getRegionCodeForCountryCode() is used because formatting information
    // for regions which share a country calling code is contained by only one
    // region for performance reasons. For example, for NANPA regions it will be
    // contained in the metadata for US.
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NSArray *regionCodes = [helper regionCodeFromCountryCode:countryCallingCode];
    NSString *regionCode = nil;
    if (regionCodes != nil && regionCodes.count > 0) {
        regionCode = [regionCodes objectAtIndex:0];
    }
    
    // Metadata cannot be nil because the country calling code is valid
    /** @type {i18n.phonenumbers.PhoneMetadata} */
    NBPhoneMetaData *metadata = [self getMetadataForRegionOrCallingCode:countryCallingCode regionCode:regionCode];
    
    NSString *formattedNumber = @"";
    NBNumberFormat *formattingPattern = [self chooseFormattingPatternForNumber:userDefinedFormats
                                                                nationalNumber:nationalSignificantNumber];
    
    if (formattingPattern == nil) {
        // If no pattern above is matched, we format the number as a whole.
        formattedNumber = nationalSignificantNumber;
    } else {
        // Before we do a replacement of the national prefix pattern $NP with the
        // national prefix, we need to copy the rule so that subsequent replacements
        // for different numbers have the appropriate national prefix.
        NBNumberFormat *numFormatCopy = [formattingPattern copy];
        NSString *nationalPrefixFormattingRule = formattingPattern.nationalPrefixFormattingRule;
        
        if (nationalPrefixFormattingRule.length > 0) {
            NSString *nationalPrefix = metadata.nationalPrefix;
            if (nationalPrefix.length > 0) {
                // Replace $NP with national prefix and $FG with the first group ($1).
                nationalPrefixFormattingRule = [self replaceStringByRegex:nationalPrefixFormattingRule
                                                                    regex:NP_PATTERN withTemplate:nationalPrefix];
                nationalPrefixFormattingRule = [self replaceStringByRegex:nationalPrefixFormattingRule
                                                                    regex:FG_PATTERN withTemplate:@"\\$1"];
                numFormatCopy.nationalPrefixFormattingRule = nationalPrefixFormattingRule;
            } else {
                // We don't want to have a rule for how to format the national prefix if
                // there isn't one.
                numFormatCopy.nationalPrefixFormattingRule = @"";
            }
        }
        
        formattedNumber = [self formatNsnUsingPattern:nationalSignificantNumber
                                    formattingPattern:numFormatCopy numberFormat:numberFormat carrierCode:nil];
    }
    
    NSString *formattedExtension = [self maybeGetFormattedExtension:number metadata:metadata numberFormat:numberFormat];
    
    //NSLog(@"!@#  prefixNumberWithCountryCallingCode called [%@]", formattedExtension);
    return [self prefixNumberWithCountryCallingCode:countryCallingCode phoneNumberFormat:numberFormat
                            formattedNationalNumber:formattedNumber formattedExtension:formattedExtension];
}


/**
 * Formats a phone number in national format for dialing using the carrier as
 * specified in the {@code carrierCode}. The {@code carrierCode} will always be
 * used regardless of whether the phone number already has a preferred domestic
 * carrier code stored. If {@code carrierCode} contains an empty string, returns
 * the number in national format without any carrier code.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be
 *     formatted.
 * @param {string} carrierCode the carrier selection code to be used.
 * @return {string} the formatted phone number in national format for dialing
 *     using the carrier as specified in the {@code carrierCode}.
 */
- (NSString *)formatNationalNumberWithCarrierCode:(NBPhoneNumber*)number carrierCode:(NSString *)carrierCode error:(NSError **)error
{
    NSString *res = nil;
    @try {
        res = [self formatNationalNumberWithCarrierCode:number carrierCode:carrierCode];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL) {
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
        }
    }
    return res;
}


- (NSString *)formatNationalNumberWithCarrierCode:(NBPhoneNumber*)number carrierCode:(NSString *)carrierCode
{
    NSNumber *countryCallingCode = number.countryCode;
    NSString *nationalSignificantNumber = [self getNationalSignificantNumber:number];
    
    if ([self hasValidCountryCallingCode:countryCallingCode] == NO) {
        return nationalSignificantNumber;
    }
    
    // Note getRegionCodeForCountryCode() is used because formatting information
    // for regions which share a country calling code is contained by only one
    // region for performance reasons. For example, for NANPA regions it will be
    // contained in the metadata for US.
    NSString *regionCode = [self getRegionCodeForCountryCode:countryCallingCode];
    // Metadata cannot be nil because the country calling code is valid.
    NBPhoneMetaData *metadata = [self getMetadataForRegionOrCallingCode:countryCallingCode regionCode:regionCode];
    NSString *formattedExtension = [self maybeGetFormattedExtension:number metadata:metadata numberFormat:NBEPhoneNumberFormatNATIONAL];
    NSString *formattedNationalNumber = [self formatNsn:nationalSignificantNumber metadata:metadata
                                      phoneNumberFormat:NBEPhoneNumberFormatNATIONAL carrierCode:carrierCode];
    return [self prefixNumberWithCountryCallingCode:countryCallingCode phoneNumberFormat:NBEPhoneNumberFormatNATIONAL
                            formattedNationalNumber:formattedNationalNumber formattedExtension:formattedExtension];
}


/**
 * @param {number} countryCallingCode
 * @param {?string} regionCode
 * @return {i18n.phonenumbers.PhoneMetadata}
 * @private
 */
- (NBPhoneMetaData*)getMetadataForRegionOrCallingCode:(NSNumber*)countryCallingCode regionCode:(NSString *)regionCode
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    
    return [NB_REGION_CODE_FOR_NON_GEO_ENTITY isEqualToString:regionCode] ?
        [helper getMetadataForNonGeographicalRegion:countryCallingCode] : [helper getMetadataForRegion:regionCode];
}


/**
 * Formats a phone number in national format for dialing using the carrier as
 * specified in the preferred_domestic_carrier_code field of the PhoneNumber
 * object passed in. If that is missing, use the {@code fallbackCarrierCode}
 * passed in instead. If there is no {@code preferred_domestic_carrier_code},
 * and the {@code fallbackCarrierCode} contains an empty string, return the
 * number in national format without any carrier code.
 *
 * <p>Use {@link #formatNationalNumberWithCarrierCode} instead if the carrier
 * code passed in should take precedence over the number's
 * {@code preferred_domestic_carrier_code} when formatting.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be
 *     formatted.
 * @param {string} fallbackCarrierCode the carrier selection code to be used, if
 *     none is found in the phone number itself.
 * @return {string} the formatted phone number in national format for dialing
 *     using the number's preferred_domestic_carrier_code, or the
 *     {@code fallbackCarrierCode} passed in if none is found.
 */
- (NSString *)formatNationalNumberWithPreferredCarrierCode:(NBPhoneNumber*)number
                                      fallbackCarrierCode:(NSString *)fallbackCarrierCode error:(NSError **)error
{
    NSString *res = nil;
    @try {
        res = [self formatNationalNumberWithCarrierCode:number carrierCode:fallbackCarrierCode];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL) {
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
        }
    }
    
    return res;
}


- (NSString *)formatNationalNumberWithPreferredCarrierCode:(NBPhoneNumber*)number fallbackCarrierCode:(NSString *)fallbackCarrierCode
{
    NSString *domesticCarrierCode = number.preferredDomesticCarrierCode != nil ? number.preferredDomesticCarrierCode : fallbackCarrierCode;
    return [self formatNationalNumberWithCarrierCode:number carrierCode:domesticCarrierCode];
}


/**
 * Returns a number formatted in such a way that it can be dialed from a mobile
 * phone in a specific region. If the number cannot be reached from the region
 * (e.g. some countries block toll-free numbers from being called outside of the
 * country), the method returns an empty string.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be
 *     formatted.
 * @param {string} regionCallingFrom the region where the call is being placed.
 * @param {boolean} withFormatting whether the number should be returned with
 *     formatting symbols, such as spaces and dashes.
 * @return {string} the formatted phone number.
 */
- (NSString *)formatNumberForMobileDialing:(NBPhoneNumber*)number regionCallingFrom:(NSString *)regionCallingFrom withFormatting:(BOOL)withFormatting
                                    error:(NSError**)error
{
    NSString *res = nil;
    @try {
        res = [self formatNumberForMobileDialing:number regionCallingFrom:regionCallingFrom withFormatting:withFormatting];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    return res;
}


- (NSString *)formatNumberForMobileDialing:(NBPhoneNumber*)number regionCallingFrom:(NSString *)regionCallingFrom withFormatting:(BOOL)withFormatting
{
    NSNumber *countryCallingCode = number.countryCode;
    
    if ([self hasValidCountryCallingCode:countryCallingCode] == NO) {
        return [NBMetadataHelper hasValue:number.rawInput] ? number.rawInput : @"";
    }
    
    NSString *formattedNumber = @"";
    // Clear the extension, as that part cannot normally be dialed together with
    // the main number.
    NBPhoneNumber *numberNoExt = [number copy];
    numberNoExt.extension = @"";
    
    NSString *regionCode = [self getRegionCodeForCountryCode:countryCallingCode];
    if ([regionCallingFrom isEqualToString:regionCode]) {
        NBEPhoneNumberType numberType = [self getNumberType:numberNoExt];
        BOOL isFixedLineOrMobile = (numberType == NBEPhoneNumberTypeFIXED_LINE) || (numberType == NBEPhoneNumberTypeMOBILE) ||
            (numberType == NBEPhoneNumberTypeFIXED_LINE_OR_MOBILE);
        // Carrier codes may be needed in some countries. We handle this here.
        if ([regionCode isEqualToString:@"CO"] && numberType == NBEPhoneNumberTypeFIXED_LINE) {
            formattedNumber = [self formatNationalNumberWithCarrierCode:numberNoExt
                                                            carrierCode:COLOMBIA_MOBILE_TO_FIXED_LINE_PREFIX];
        } else if ([regionCode isEqualToString:@"BR"] && isFixedLineOrMobile) {
            formattedNumber = [NBMetadataHelper hasValue:numberNoExt.preferredDomesticCarrierCode] ?
            [self formatNationalNumberWithPreferredCarrierCode:numberNoExt fallbackCarrierCode:@""] : @"";
            // Brazilian fixed line and mobile numbers need to be dialed with a
            // carrier code when called within Brazil. Without that, most of the
            // carriers won't connect the call. Because of that, we return an
            // empty string here.
        } else {
            // For NANPA countries, non-geographical countries, and Mexican fixed
            // line and mobile numbers, we output international format for numbersi
            // that can be dialed internationally as that always works.
            if ((countryCallingCode.unsignedIntegerValue == NANPA_COUNTRY_CODE_ ||
                 [regionCode isEqualToString:NB_REGION_CODE_FOR_NON_GEO_ENTITY] ||
                 // MX fixed line and mobile numbers should always be formatted in
                 // international format, even when dialed within MX. For national
                 // format to work, a carrier code needs to be used, and the correct
                 // carrier code depends on if the caller and callee are from the
                 // same local area. It is trickier to get that to work correctly than
                 // using international format, which is tested to work fine on all
                 // carriers.
                 ([regionCode isEqualToString:@"MX"] && isFixedLineOrMobile)) && [self canBeInternationallyDialled:numberNoExt]) {
                formattedNumber = [self format:numberNoExt numberFormat:NBEPhoneNumberFormatINTERNATIONAL];
            } else {
                formattedNumber = [self format:numberNoExt numberFormat:NBEPhoneNumberFormatNATIONAL];
            }
        }
    } else if ([self canBeInternationallyDialled:numberNoExt]) {
        return withFormatting ? [self format:numberNoExt numberFormat:NBEPhoneNumberFormatINTERNATIONAL] :
        [self format:numberNoExt numberFormat:NBEPhoneNumberFormatE164];
    }
    
    return withFormatting ?
        formattedNumber : [self normalizeHelper:formattedNumber normalizationReplacements:DIALLABLE_CHAR_MAPPINGS removeNonMatches:YES];
}


/**
 * Formats a phone number for out-of-country dialing purposes. If no
 * regionCallingFrom is supplied, we format the number in its INTERNATIONAL
 * format. If the country calling code is the same as that of the region where
 * the number is from, then NATIONAL formatting will be applied.
 *
 * <p>If the number itself has a country calling code of zero or an otherwise
 * invalid country calling code, then we return the number with no formatting
 * applied.
 *
 * <p>Note this function takes care of the case for calling inside of NANPA and
 * between Russia and Kazakhstan (who share the same country calling code). In
 * those cases, no international prefix is used. For regions which have multiple
 * international prefixes, the number in its INTERNATIONAL format will be
 * returned instead.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be
 *     formatted.
 * @param {string} regionCallingFrom the region where the call is being placed.
 * @return {string} the formatted phone number.
 */
- (NSString *)formatOutOfCountryCallingNumber:(NBPhoneNumber*)number regionCallingFrom:(NSString *)regionCallingFrom error:(NSError**)error
{
    NSString *res = nil;
    @try {
        res = [self formatOutOfCountryCallingNumber:number regionCallingFrom:regionCallingFrom];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    
    return res;
}

- (NSString *)formatOutOfCountryCallingNumber:(NBPhoneNumber*)number regionCallingFrom:(NSString *)regionCallingFrom
{
    if ([self isValidRegionCode:regionCallingFrom] == NO) {
        return [self format:number numberFormat:NBEPhoneNumberFormatINTERNATIONAL];
    }
    
    NSNumber *countryCallingCode = [number.countryCode copy];
    NSString *nationalSignificantNumber = [self getNationalSignificantNumber:number];
    
    if ([self hasValidCountryCallingCode:countryCallingCode] == NO) {
        return nationalSignificantNumber;
    }
    
    if (countryCallingCode.unsignedIntegerValue == NANPA_COUNTRY_CODE_) {
        if ([self isNANPACountry:regionCallingFrom]) {
            // For NANPA regions, return the national format for these regions but
            // prefix it with the country calling code.
            return [NSString stringWithFormat:@"%@ %@", countryCallingCode, [self format:number numberFormat:NBEPhoneNumberFormatNATIONAL]];
        }
    } else if ([countryCallingCode isEqualToNumber:[self getCountryCodeForValidRegion:regionCallingFrom error:nil]]) {
        // If regions share a country calling code, the country calling code need
        // not be dialled. This also applies when dialling within a region, so this
        // if clause covers both these cases. Technically this is the case for
        // dialling from La Reunion to other overseas departments of France (French
        // Guiana, Martinique, Guadeloupe), but not vice versa - so we don't cover
        // this edge case for now and for those cases return the version including
        // country calling code. Details here:
        // http://www.petitfute.com/voyage/225-info-pratiques-reunion
        return [self format:number numberFormat:NBEPhoneNumberFormatNATIONAL];
    }
    
    // Metadata cannot be nil because we checked 'isValidRegionCode()' above.
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NBPhoneMetaData *metadataForRegionCallingFrom = [helper getMetadataForRegion:regionCallingFrom];
    NSString *internationalPrefix = metadataForRegionCallingFrom.internationalPrefix;
    
    // For regions that have multiple international prefixes, the international
    // format of the number is returned, unless there is a preferred international
    // prefix.
    NSString *internationalPrefixForFormatting = @"";
    
    if ([self matchesEntirely:UNIQUE_INTERNATIONAL_PREFIX string:internationalPrefix]) {
        internationalPrefixForFormatting = internationalPrefix;
    } else if ([NBMetadataHelper hasValue:metadataForRegionCallingFrom.preferredInternationalPrefix]) {
        internationalPrefixForFormatting = metadataForRegionCallingFrom.preferredInternationalPrefix;
    }
    
    NSString *regionCode = [self getRegionCodeForCountryCode:countryCallingCode];
    // Metadata cannot be nil because the country calling code is valid.
    NBPhoneMetaData *metadataForRegion = [self getMetadataForRegionOrCallingCode:countryCallingCode regionCode:regionCode];
    NSString *formattedNationalNumber = [self formatNsn:nationalSignificantNumber metadata:metadataForRegion
                                      phoneNumberFormat:NBEPhoneNumberFormatINTERNATIONAL carrierCode:nil];
    NSString *formattedExtension = [self maybeGetFormattedExtension:number metadata:metadataForRegion numberFormat:NBEPhoneNumberFormatINTERNATIONAL];
    
    NSString *hasLenth = [NSString stringWithFormat:@"%@ %@ %@%@", internationalPrefixForFormatting, countryCallingCode, formattedNationalNumber, formattedExtension];
    NSString *hasNotLength = [self prefixNumberWithCountryCallingCode:countryCallingCode phoneNumberFormat:NBEPhoneNumberFormatINTERNATIONAL
                                              formattedNationalNumber:formattedNationalNumber formattedExtension:formattedExtension];
    
    return internationalPrefixForFormatting.length > 0 ? hasLenth:hasNotLength;
}


/**
 * A helper function that is used by format and formatByPattern.
 *
 * @param {number} countryCallingCode the country calling code.
 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
 *     phone number should be formatted into.
 * @param {string} formattedNationalNumber
 * @param {string} formattedExtension
 * @return {string} the formatted phone number.
 * @private
 */
- (NSString *)prefixNumberWithCountryCallingCode:(NSNumber*)countryCallingCode phoneNumberFormat:(NBEPhoneNumberFormat)numberFormat
                        formattedNationalNumber:(NSString *)formattedNationalNumber
                             formattedExtension:(NSString *)formattedExtension
{
    switch (numberFormat)
    {
        case NBEPhoneNumberFormatE164:
            return [NSString stringWithFormat:@"+%@%@%@", countryCallingCode, formattedNationalNumber, formattedExtension];
        case NBEPhoneNumberFormatINTERNATIONAL:
            return [NSString stringWithFormat:@"+%@ %@%@", countryCallingCode, formattedNationalNumber, formattedExtension];
        case NBEPhoneNumberFormatRFC3966:
            return [NSString stringWithFormat:@"%@+%@-%@%@", RFC3966_PREFIX, countryCallingCode, formattedNationalNumber, formattedExtension];
        case NBEPhoneNumberFormatNATIONAL:
        default:
            return [NSString stringWithFormat:@"%@%@", formattedNationalNumber, formattedExtension];
    }
}


/**
 * Formats a phone number using the original phone number format that the number
 * is parsed from. The original format is embedded in the country_code_source
 * field of the PhoneNumber object passed in. If such information is missing,
 * the number will be formatted into the NATIONAL format by default. When the
 * number contains a leading zero and this is unexpected for this country, or we
 * don't have a formatting pattern for the number, the method returns the raw
 * input when it is available.
 *
 * Note this method guarantees no digit will be inserted, removed or modified as
 * a result of formatting.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number that needs to
 *     be formatted in its original number format.
 * @param {string} regionCallingFrom the region whose IDD needs to be prefixed
 *     if the original number has one.
 * @return {string} the formatted phone number in its original number format.
 */
- (NSString *)formatInOriginalFormat:(NBPhoneNumber*)number regionCallingFrom:(NSString *)regionCallingFrom error:(NSError **)error
{
    NSString *res = nil;
    @try {
        res = [self formatInOriginalFormat:number regionCallingFrom:regionCallingFrom];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    
    return res;
}


- (NSString *)formatInOriginalFormat:(NBPhoneNumber*)number regionCallingFrom:(NSString *)regionCallingFrom
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    
    if ([NBMetadataHelper hasValue:number.rawInput] && ([self hasUnexpectedItalianLeadingZero:number] || [self hasFormattingPatternForNumber:number] == NO)) {
        // We check if we have the formatting pattern because without that, we might
        // format the number as a group without national prefix.
        return number.rawInput;
    }
    
    if (number.countryCodeSource == nil) {
        return [self format:number numberFormat:NBEPhoneNumberFormatNATIONAL];
    }
    
    NSString *formattedNumber = @"";
    
    switch ([number.countryCodeSource intValue])
    {
        case NBECountryCodeSourceFROM_NUMBER_WITH_PLUS_SIGN:
            formattedNumber = [self format:number numberFormat:NBEPhoneNumberFormatINTERNATIONAL];
            break;
        case NBECountryCodeSourceFROM_NUMBER_WITH_IDD:
            formattedNumber = [self formatOutOfCountryCallingNumber:number regionCallingFrom:regionCallingFrom];
            break;
        case NBECountryCodeSourceFROM_NUMBER_WITHOUT_PLUS_SIGN:
            formattedNumber = [[self format:number numberFormat:NBEPhoneNumberFormatINTERNATIONAL] substringFromIndex:1];
            break;
        case NBECountryCodeSourceFROM_DEFAULT_COUNTRY:
            // Fall-through to default case.
        default:
        {
            NSString *regionCode = [self getRegionCodeForCountryCode:number.countryCode];
            // We strip non-digits from the NDD here, and from the raw input later,
            // so that we can compare them easily.
            NSString *nationalPrefix = [self getNddPrefixForRegion:regionCode stripNonDigits:YES];
            NSString *nationalFormat = [self format:number numberFormat:NBEPhoneNumberFormatNATIONAL];
            if (nationalPrefix == nil || nationalPrefix.length == 0)
            {
                // If the region doesn't have a national prefix at all, we can safely
                // return the national format without worrying about a national prefix
                // being added.
                formattedNumber = nationalFormat;
                break;
            }
            // Otherwise, we check if the original number was entered with a national
            // prefix.
            if ([self rawInputContainsNationalPrefix:number.rawInput nationalPrefix:nationalPrefix regionCode:regionCode])
            {
                // If so, we can safely return the national format.
                formattedNumber = nationalFormat;
                break;
            }
            // Metadata cannot be nil here because getNddPrefixForRegion() (above)
            // returns nil if there is no metadata for the region.
            NBPhoneMetaData *metadata = [helper getMetadataForRegion:regionCode];
            NSString *nationalNumber = [self getNationalSignificantNumber:number];
            NBNumberFormat *formatRule = [self chooseFormattingPatternForNumber:metadata.numberFormats nationalNumber:nationalNumber];
            // The format rule could still be nil here if the national number was 0
            // and there was no raw input (this should not be possible for numbers
            // generated by the phonenumber library as they would also not have a
            // country calling code and we would have exited earlier).
            if (formatRule == nil)
            {
                formattedNumber = nationalFormat;
                break;
            }
            // When the format we apply to this number doesn't contain national
            // prefix, we can just return the national format.
            // TODO: Refactor the code below with the code in
            // isNationalPrefixPresentIfRequired.
            NSString *candidateNationalPrefixRule = formatRule.nationalPrefixFormattingRule;
            // We assume that the first-group symbol will never be _before_ the
            // national prefix.
            NSRange firstGroupRange = [candidateNationalPrefixRule rangeOfString:@"$1"];
            if (firstGroupRange.location == NSNotFound)
            {
                formattedNumber = nationalFormat;
                break;
            }
            
            if (firstGroupRange.location <= 0)
            {
                formattedNumber = nationalFormat;
                break;
            }
            candidateNationalPrefixRule = [candidateNationalPrefixRule substringWithRange:NSMakeRange(0, firstGroupRange.location)];
            candidateNationalPrefixRule = [self normalizeDigitsOnly:candidateNationalPrefixRule];
            if (candidateNationalPrefixRule.length == 0)
            {
                // National prefix not used when formatting this number.
                formattedNumber = nationalFormat;
                break;
            }
            // Otherwise, we need to remove the national prefix from our output.
            NBNumberFormat *numFormatCopy = [formatRule copy];
            numFormatCopy.nationalPrefixFormattingRule = nil;
            formattedNumber = [self formatByPattern:number numberFormat:NBEPhoneNumberFormatNATIONAL userDefinedFormats:@[numFormatCopy]];
            break;
        }
    }
    
    NSString *rawInput = number.rawInput;
    // If no digit is inserted/removed/modified as a result of our formatting, we
    // return the formatted phone number; otherwise we return the raw input the
    // user entered.
    if (formattedNumber != nil && rawInput.length > 0)
    {
        NSString *normalizedFormattedNumber = [self normalizeHelper:formattedNumber normalizationReplacements:DIALLABLE_CHAR_MAPPINGS removeNonMatches:YES];
        /** @type {string} */
        NSString *normalizedRawInput = [self normalizeHelper:rawInput normalizationReplacements:DIALLABLE_CHAR_MAPPINGS removeNonMatches:YES];
        
        if ([normalizedFormattedNumber isEqualToString:normalizedRawInput] == NO)
        {
            formattedNumber = rawInput;
        }
    }
    return formattedNumber;
}


/**
 * Check if rawInput, which is assumed to be in the national format, has a
 * national prefix. The national prefix is assumed to be in digits-only form.
 * @param {string} rawInput
 * @param {string} nationalPrefix
 * @param {string} regionCode
 * @return {boolean}
 * @private
 */
- (BOOL)rawInputContainsNationalPrefix:(NSString *)rawInput nationalPrefix:(NSString *)nationalPrefix regionCode:(NSString *)regionCode
{
    BOOL isValid = NO;
    NSString *normalizedNationalNumber = [self normalizeDigitsOnly:rawInput];
    if ([self isStartingStringByRegex:normalizedNationalNumber regex:nationalPrefix])
    {
        // Some Japanese numbers (e.g. 00777123) might be mistaken to contain the
        // national prefix when written without it (e.g. 0777123) if we just do
        // prefix matching. To tackle that, we check the validity of the number if
        // the assumed national prefix is removed (777123 won't be valid in
        // Japan).
        NSString *subString = [normalizedNationalNumber substringFromIndex:nationalPrefix.length];
        NSError *anError = nil;
        isValid = [self isValidNumber:[self parse:subString defaultRegion:regionCode error:&anError]];
        
        if (anError != nil)
            return NO;
    }
    return isValid;
}


/**
 * Returns NO if a number is from a region whose national significant number
 * couldn't contain a leading zero, but has the italian_leading_zero field set
 * to NO.
 * @param {i18n.phonenumbers.PhoneNumber} number
 * @return {boolean}
 * @private
 */
- (BOOL)hasUnexpectedItalianLeadingZero:(NBPhoneNumber*)number
{
    return number.italianLeadingZero && [self isLeadingZeroPossible:number.countryCode] == NO;
}


/**
 * @param {i18n.phonenumbers.PhoneNumber} number
 * @return {boolean}
 * @private
 */
- (BOOL)hasFormattingPatternForNumber:(NBPhoneNumber*)number
{
    NSNumber *countryCallingCode = number.countryCode;
    NSString *phoneNumberRegion = [self getRegionCodeForCountryCode:countryCallingCode];
    NBPhoneMetaData *metadata = [self getMetadataForRegionOrCallingCode:countryCallingCode regionCode:phoneNumberRegion];
    
    if (metadata == nil)
    {
        return NO;
    }
    
    NSString *nationalNumber = [self getNationalSignificantNumber:number];
    NBNumberFormat *formatRule = [self chooseFormattingPatternForNumber:metadata.numberFormats nationalNumber:nationalNumber];
    return formatRule != nil;
}


/**
 * Formats a phone number for out-of-country dialing purposes.
 *
 * Note that in this version, if the number was entered originally using alpha
 * characters and this version of the number is stored in raw_input, this
 * representation of the number will be used rather than the digit
 * representation. Grouping information, as specified by characters such as '-'
 * and ' ', will be retained.
 *
 * <p><b>Caveats:</b></p>
 * <ul>
 * <li>This will not produce good results if the country calling code is both
 * present in the raw input _and_ is the start of the national number. This is
 * not a problem in the regions which typically use alpha numbers.
 * <li>This will also not produce good results if the raw input has any grouping
 * information within the first three digits of the national number, and if the
 * function needs to strip preceding digits/words in the raw input before these
 * digits. Normally people group the first three digits together so this is not
 * a huge problem - and will be fixed if it proves to be so.
 * </ul>
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number that needs to
 *     be formatted.
 * @param {string} regionCallingFrom the region where the call is being placed.
 * @return {string} the formatted phone number.
 */
- (NSString *)formatOutOfCountryKeepingAlphaChars:(NBPhoneNumber*)number regionCallingFrom:(NSString *)regionCallingFrom error:(NSError **)error
{
    NSString *res = nil;
    @try {
        res = [self formatOutOfCountryKeepingAlphaChars:number regionCallingFrom:regionCallingFrom];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    return res;
}


- (NSString *)formatOutOfCountryKeepingAlphaChars:(NBPhoneNumber*)number regionCallingFrom:(NSString *)regionCallingFrom
{
    NSString *rawInput = number.rawInput;
    // If there is no raw input, then we can't keep alpha characters because there
    // aren't any. In this case, we return formatOutOfCountryCallingNumber.
    if (rawInput == nil || rawInput.length == 0)
    {
        return [self formatOutOfCountryCallingNumber:number regionCallingFrom:regionCallingFrom];
    }
    
    NSNumber *countryCode = number.countryCode;
    if ([self hasValidCountryCallingCode:countryCode] == NO)
    {
        return rawInput;
    }
    // Strip any prefix such as country calling code, IDD, that was present. We do
    // this by comparing the number in raw_input with the parsed number. To do
    // this, first we normalize punctuation. We retain number grouping symbols
    // such as ' ' only.
    rawInput = [self normalizeHelper:rawInput normalizationReplacements:ALL_PLUS_NUMBER_GROUPING_SYMBOLS removeNonMatches:NO];
    //NSLog(@"---- formatOutOfCountryKeepingAlphaChars normalizeHelper rawInput [%@]", rawInput);
    // Now we trim everything before the first three digits in the parsed number.
    // We choose three because all valid alpha numbers have 3 digits at the start
    // - if it does not, then we don't trim anything at all. Similarly, if the
    // national number was less than three digits, we don't trim anything at all.
    NSString *nationalNumber = [self getNationalSignificantNumber:number];
    if (nationalNumber.length > 3)
    {
        int firstNationalNumberDigit = [self indexOfStringByString:rawInput target:[nationalNumber substringWithRange:NSMakeRange(0, 3)]];
        if (firstNationalNumberDigit != -1)
        {
            rawInput = [rawInput substringFromIndex:firstNationalNumberDigit];
        }
    }
    
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NBPhoneMetaData *metadataForRegionCallingFrom = [helper getMetadataForRegion:regionCallingFrom];
    
    if (countryCode.unsignedIntegerValue == NANPA_COUNTRY_CODE_) {
        if ([self isNANPACountry:regionCallingFrom]) {
            return [NSString stringWithFormat:@"%@ %@", countryCode, rawInput];
        }
    } else if (metadataForRegionCallingFrom != nil && [countryCode isEqualToNumber:[self getCountryCodeForValidRegion:regionCallingFrom error:nil]]) {
        NBNumberFormat *formattingPattern = [self chooseFormattingPatternForNumber:metadataForRegionCallingFrom.numberFormats
                                                                    nationalNumber:nationalNumber];
        if (formattingPattern == nil) {
            // If no pattern above is matched, we format the original input.
            return rawInput;
        }
        
        NBNumberFormat *newFormat = [formattingPattern copy];
        // The first group is the first group of digits that the user wrote
        // together.
        newFormat.pattern = @"(\\d+)(.*)";
        // Here we just concatenate them back together after the national prefix
        // has been fixed.
        newFormat.format = @"$1$2";
        // Now we format using this pattern instead of the default pattern, but
        // with the national prefix prefixed if necessary.
        // This will not work in the cases where the pattern (and not the leading
        // digits) decide whether a national prefix needs to be used, since we have
        // overridden the pattern to match anything, but that is not the case in the
        // metadata to date.
        
        return [self formatNsnUsingPattern:rawInput formattingPattern:newFormat numberFormat:NBEPhoneNumberFormatNATIONAL carrierCode:nil];
    }
    
    NSString *internationalPrefixForFormatting = @"";
    // If an unsupported region-calling-from is entered, or a country with
    // multiple international prefixes, the international format of the number is
    // returned, unless there is a preferred international prefix.
    if (metadataForRegionCallingFrom != nil) {
        NSString *internationalPrefix = metadataForRegionCallingFrom.internationalPrefix;
        internationalPrefixForFormatting =
        [self matchesEntirely:UNIQUE_INTERNATIONAL_PREFIX string:internationalPrefix] ? internationalPrefix : metadataForRegionCallingFrom.preferredInternationalPrefix;
    }
    
    NSString *regionCode = [self getRegionCodeForCountryCode:countryCode];
    // Metadata cannot be nil because the country calling code is valid.
    NBPhoneMetaData *metadataForRegion = [self getMetadataForRegionOrCallingCode:countryCode regionCode:regionCode];
    NSString *formattedExtension = [self maybeGetFormattedExtension:number metadata:metadataForRegion numberFormat:NBEPhoneNumberFormatINTERNATIONAL];
    
    if (internationalPrefixForFormatting.length > 0) {
        return [NSString stringWithFormat:@"%@ %@ %@%@", internationalPrefixForFormatting, countryCode, rawInput, formattedExtension];
    } else {
        // Invalid region entered as country-calling-from (so no metadata was found
        // for it) or the region chosen has multiple international dialling
        // prefixes.
        return [self prefixNumberWithCountryCallingCode:countryCode phoneNumberFormat:NBEPhoneNumberFormatINTERNATIONAL formattedNationalNumber:rawInput formattedExtension:formattedExtension];
    }
}


/**
 * Note in some regions, the national number can be written in two completely
 * different ways depending on whether it forms part of the NATIONAL format or
 * INTERNATIONAL format. The numberFormat parameter here is used to specify
 * which format to use for those cases. If a carrierCode is specified, this will
 * be inserted into the formatted string to replace $CC.
 *
 * @param {string} number a string of characters representing a phone number.
 * @param {i18n.phonenumbers.PhoneMetadata} metadata the metadata for the
 *     region that we think this number is from.
 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
 *     phone number should be formatted into.
 * @param {string=} opt_carrierCode
 * @return {string} the formatted phone number.
 * @private
 */
- (NSString *)formatNsn:(NSString *)phoneNumber metadata:(NBPhoneMetaData*)metadata phoneNumberFormat:(NBEPhoneNumberFormat)numberFormat carrierCode:(NSString *)opt_carrierCode
{
    NSMutableArray *intlNumberFormats = metadata.intlNumberFormats;
    // When the intlNumberFormats exists, we use that to format national number
    // for the INTERNATIONAL format instead of using the numberDesc.numberFormats.
    NSArray *availableFormats = ([intlNumberFormats count] <= 0 || numberFormat == NBEPhoneNumberFormatNATIONAL) ? metadata.numberFormats : intlNumberFormats;
    NBNumberFormat *formattingPattern = [self chooseFormattingPatternForNumber:availableFormats nationalNumber:phoneNumber];
    
    if (formattingPattern == nil) {
        return phoneNumber;
    }
    
    return [self formatNsnUsingPattern:phoneNumber formattingPattern:formattingPattern numberFormat:numberFormat carrierCode:opt_carrierCode];
}


/**
 * @param {Array.<i18n.phonenumbers.NumberFormat>} availableFormats the
 *     available formats the phone number could be formatted into.
 * @param {string} nationalNumber a string of characters representing a phone
 *     number.
 * @return {i18n.phonenumbers.NumberFormat}
 * @private
 */
- (NBNumberFormat*)chooseFormattingPatternForNumber:(NSArray*)availableFormats nationalNumber:(NSString *)nationalNumber
{
    for (NBNumberFormat *numFormat in availableFormats) {
        unsigned int size = (unsigned int)[numFormat.leadingDigitsPatterns count];
        // We always use the last leading_digits_pattern, as it is the most detailed.
        if (size == 0 || [self stringPositionByRegex:nationalNumber regex:[numFormat.leadingDigitsPatterns lastObject]] == 0) {
            if ([self matchesEntirely:numFormat.pattern string:nationalNumber]) {
                return numFormat;
            }
        }
    }
    
    return nil;
}


/**
 * Note that carrierCode is optional - if nil or an empty string, no carrier
 * code replacement will take place.
 *
 * @param {string} nationalNumber a string of characters representing a phone
 *     number.
 * @param {i18n.phonenumbers.NumberFormat} formattingPattern the formatting rule
 *     the phone number should be formatted into.
 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
 *     phone number should be formatted into.
 * @param {string=} opt_carrierCode
 * @return {string} the formatted phone number.
 * @private
 */
- (NSString *)formatNsnUsingPattern:(NSString *)nationalNumber formattingPattern:(NBNumberFormat*)formattingPattern numberFormat:(NBEPhoneNumberFormat)numberFormat carrierCode:(NSString *)opt_carrierCode
{
    NSString *numberFormatRule = formattingPattern.format;
    NSString *domesticCarrierCodeFormattingRule = formattingPattern.domesticCarrierCodeFormattingRule;
    NSString *formattedNationalNumber = @"";
    
    if (numberFormat == NBEPhoneNumberFormatNATIONAL && [NBMetadataHelper hasValue:opt_carrierCode] && domesticCarrierCodeFormattingRule.length > 0) {
        // Replace the $CC in the formatting rule with the desired carrier code.
        NSString *carrierCodeFormattingRule = [self replaceStringByRegex:domesticCarrierCodeFormattingRule regex:CC_PATTERN withTemplate:opt_carrierCode];
        // Now replace the $FG in the formatting rule with the first group and
        // the carrier code combined in the appropriate way.
        numberFormatRule = [self replaceFirstStringByRegex:numberFormatRule regex:FIRST_GROUP_PATTERN
                                              withTemplate:carrierCodeFormattingRule];
        formattedNationalNumber = [self replaceStringByRegex:nationalNumber regex:formattingPattern.pattern withTemplate:numberFormatRule];
    } else {
        // Use the national prefix formatting rule instead.
        NSString *nationalPrefixFormattingRule = formattingPattern.nationalPrefixFormattingRule;
        if (numberFormat == NBEPhoneNumberFormatNATIONAL && [NBMetadataHelper hasValue:nationalPrefixFormattingRule]) {
            NSString *replacePattern = [self replaceFirstStringByRegex:numberFormatRule regex:FIRST_GROUP_PATTERN withTemplate:nationalPrefixFormattingRule];
            formattedNationalNumber = [self replaceStringByRegex:nationalNumber regex:formattingPattern.pattern withTemplate:replacePattern];
        } else {
            formattedNationalNumber = [self replaceStringByRegex:nationalNumber regex:formattingPattern.pattern withTemplate:numberFormatRule];
        }
    }
    
    if (numberFormat == NBEPhoneNumberFormatRFC3966) {
        // Strip any leading punctuation.
        formattedNationalNumber = [self replaceStringByRegex:formattedNationalNumber regex:[NSString stringWithFormat:@"^%@", SEPARATOR_PATTERN] withTemplate:@""];
        
        // Replace the rest with a dash between each number group.
        formattedNationalNumber = [self replaceStringByRegex:formattedNationalNumber regex:SEPARATOR_PATTERN withTemplate:@"-"];
    }
    return formattedNationalNumber;
}


/**
 * Gets a valid number for the specified region.
 *
 * @param {string} regionCode the region for which an example number is needed.
 * @return {i18n.phonenumbers.PhoneNumber} a valid fixed-line number for the
 *     specified region. Returns nil when the metadata does not contain such
 *     information, or the region 001 is passed in. For 001 (representing non-
 *     geographical numbers), call {@link #getExampleNumberForNonGeoEntity}
 *     instead.
 */
- (NBPhoneNumber*)getExampleNumber:(NSString *)regionCode error:(NSError *__autoreleasing *)error
{
    NBPhoneNumber *res = [self getExampleNumberForType:regionCode type:NBEPhoneNumberTypeFIXED_LINE error:error];
    return res;
}


/**
 * Gets a valid number for the specified region and number type.
 *
 * @param {string} regionCode the region for which an example number is needed.
 * @param {i18n.phonenumbers.PhoneNumberType} type the type of number that is
 *     needed.
 * @return {i18n.phonenumbers.PhoneNumber} a valid number for the specified
 *     region and type. Returns nil when the metadata does not contain such
 *     information or if an invalid region or region 001 was entered.
 *     For 001 (representing non-geographical numbers), call
 *     {@link #getExampleNumberForNonGeoEntity} instead.
 */
- (NBPhoneNumber*)getExampleNumberForType:(NSString *)regionCode type:(NBEPhoneNumberType)type error:(NSError *__autoreleasing *)error
{
    NBPhoneNumber *res = nil;
    
    if ([self isValidRegionCode:regionCode] == NO) {
        return nil;
    }
    
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    
    NBPhoneNumberDesc *desc = [self getNumberDescByType:[helper getMetadataForRegion:regionCode] type:type];
    
    if ([NBMetadataHelper hasValue:desc.exampleNumber ]) {
        return [self parse:desc.exampleNumber defaultRegion:regionCode error:error];
    }
    
    return res;
}


/**
 * Gets a valid number for the specified country calling code for a
 * non-geographical entity.
 *
 * @param {number} countryCallingCode the country calling code for a
 *     non-geographical entity.
 * @return {i18n.phonenumbers.PhoneNumber} a valid number for the
 *     non-geographical entity. Returns nil when the metadata does not contain
 *     such information, or the country calling code passed in does not belong
 *     to a non-geographical entity.
 */
- (NBPhoneNumber*)getExampleNumberForNonGeoEntity:(NSNumber *)countryCallingCode error:(NSError *__autoreleasing *)error
{
    NBPhoneNumber *res = nil;
    
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NBPhoneMetaData *metadata = [helper getMetadataForNonGeographicalRegion:countryCallingCode];
    
    if (metadata != nil) {
        NBPhoneNumberDesc *desc = metadata.generalDesc;
        if ([NBMetadataHelper hasValue:desc.exampleNumber]) {
            NSString *callCode = [NSString stringWithFormat:@"+%@%@", countryCallingCode, desc.exampleNumber];
            return [self parse:callCode defaultRegion:UNKNOWN_REGION_ error:error];
        }
    }
    
    return res;
}


/**
 * Gets the formatted extension of a phone number, if the phone number had an
 * extension specified. If not, it returns an empty string.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the PhoneNumber that might have
 *     an extension.
 * @param {i18n.phonenumbers.PhoneMetadata} metadata the metadata for the
 *     region that we think this number is from.
 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
 *     phone number should be formatted into.
 * @return {string} the formatted extension if any.
 * @private
 */
- (NSString *)maybeGetFormattedExtension:(NBPhoneNumber*)number metadata:(NBPhoneMetaData*)metadata numberFormat:(NBEPhoneNumberFormat)numberFormat
{
    if ([NBMetadataHelper hasValue:number.extension] == NO) {
        return @"";
    } else {
        if (numberFormat == NBEPhoneNumberFormatRFC3966) {
            return [NSString stringWithFormat:@"%@%@", RFC3966_EXTN_PREFIX, number.extension];
        } else {
            if ([NBMetadataHelper hasValue:metadata.preferredExtnPrefix]) {
                return [NSString stringWithFormat:@"%@%@", metadata.preferredExtnPrefix, number.extension];
            } else {
                return [NSString stringWithFormat:@"%@%@", DEFAULT_EXTN_PREFIX, number.extension];
            }
        }
    }
}


/**
 * @param {i18n.phonenumbers.PhoneMetadata} metadata
 * @param {i18n.phonenumbers.PhoneNumberType} type
 * @return {i18n.phonenumbers.PhoneNumberDesc}
 * @private
 */
- (NBPhoneNumberDesc*)getNumberDescByType:(NBPhoneMetaData*)metadata type:(NBEPhoneNumberType)type
{
    switch (type)
    {
        case NBEPhoneNumberTypePREMIUM_RATE:
            return metadata.premiumRate;
        case NBEPhoneNumberTypeTOLL_FREE:
            return metadata.tollFree;
        case NBEPhoneNumberTypeMOBILE:
            if (metadata.mobile == nil) return metadata.generalDesc;
            return metadata.mobile;
        case NBEPhoneNumberTypeFIXED_LINE:
        case NBEPhoneNumberTypeFIXED_LINE_OR_MOBILE:
            if (metadata.fixedLine == nil) return metadata.generalDesc;
            return metadata.fixedLine;
        case NBEPhoneNumberTypeSHARED_COST:
            return metadata.sharedCost;
        case NBEPhoneNumberTypeVOIP:
            return metadata.voip;
        case NBEPhoneNumberTypePERSONAL_NUMBER:
            return metadata.personalNumber;
        case NBEPhoneNumberTypePAGER:
            return metadata.pager;
        case NBEPhoneNumberTypeUAN:
            return metadata.uan;
        case NBEPhoneNumberTypeVOICEMAIL:
            return metadata.voicemail;
        default:
            return metadata.generalDesc;
    }
}

/**
 * Gets the type of a phone number.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want
 *     to know the type.
 * @return {i18n.phonenumbers.PhoneNumberType} the type of the phone number.
 */
- (NBEPhoneNumberType)getNumberType:(NBPhoneNumber*)phoneNumber
{
    NSString *regionCode = [self getRegionCodeForNumber:phoneNumber];
    NBPhoneMetaData *metadata = [self getMetadataForRegionOrCallingCode:phoneNumber.countryCode regionCode:regionCode];
    if (metadata == nil)
    {
        return NBEPhoneNumberTypeUNKNOWN;
    }
    
    NSString *nationalSignificantNumber = [self getNationalSignificantNumber:phoneNumber];
    return [self getNumberTypeHelper:nationalSignificantNumber metadata:metadata];
}


/**
 * @param {string} nationalNumber
 * @param {i18n.phonenumbers.PhoneMetadata} metadata
 * @return {i18n.phonenumbers.PhoneNumberType}
 * @private
 */
- (NBEPhoneNumberType)getNumberTypeHelper:(NSString *)nationalNumber metadata:(NBPhoneMetaData*)metadata
{
    NBPhoneNumberDesc *generalNumberDesc = metadata.generalDesc;
    
    //NSLog(@"getNumberTypeHelper - UNKNOWN 1");
    if ([NBMetadataHelper hasValue:generalNumberDesc.nationalNumberPattern] == NO ||
        [self isNumberMatchingDesc:nationalNumber numberDesc:generalNumberDesc] == NO)
    {
        //NSLog(@"getNumberTypeHelper - UNKNOWN 2");
        return NBEPhoneNumberTypeUNKNOWN;
    }
    
    //NSLog(@"getNumberTypeHelper - PREMIUM_RATE 1");
    if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.premiumRate])
    {
        //NSLog(@"getNumberTypeHelper - PREMIUM_RATE 2");
        return NBEPhoneNumberTypePREMIUM_RATE;
    }
    
    //NSLog(@"getNumberTypeHelper - TOLL_FREE 1");
    if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.tollFree])
    {
        //NSLog(@"getNumberTypeHelper - TOLL_FREE 2");
        return NBEPhoneNumberTypeTOLL_FREE;
    }
    
    //NSLog(@"getNumberTypeHelper - SHARED_COST 1");
    if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.sharedCost])
    {
        //NSLog(@"getNumberTypeHelper - SHARED_COST 2");
        return NBEPhoneNumberTypeSHARED_COST;
    }
    
    //NSLog(@"getNumberTypeHelper - VOIP 1");
    if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.voip])
    {
        //NSLog(@"getNumberTypeHelper - VOIP 2");
        return NBEPhoneNumberTypeVOIP;
    }
    
    //NSLog(@"getNumberTypeHelper - PERSONAL_NUMBER 1");
    if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.personalNumber])
    {
        //NSLog(@"getNumberTypeHelper - PERSONAL_NUMBER 2");
        return NBEPhoneNumberTypePERSONAL_NUMBER;
    }
    
    //NSLog(@"getNumberTypeHelper - PAGER 1");
    if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.pager])
    {
        //NSLog(@"getNumberTypeHelper - PAGER 2");
        return NBEPhoneNumberTypePAGER;
    }
    
    //NSLog(@"getNumberTypeHelper - UAN 1");
    if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.uan])
    {
        //NSLog(@"getNumberTypeHelper - UAN 2");
        return NBEPhoneNumberTypeUAN;
    }
    
    //NSLog(@"getNumberTypeHelper - VOICEMAIL 1");
    if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.voicemail])
    {
        //NSLog(@"getNumberTypeHelper - VOICEMAIL 2");
        return NBEPhoneNumberTypeVOICEMAIL;
    }
    
    if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.fixedLine])
    {
        if (metadata.sameMobileAndFixedLinePattern)
        {
            //NSLog(@"getNumberTypeHelper - FIXED_LINE_OR_MOBILE");
            return NBEPhoneNumberTypeFIXED_LINE_OR_MOBILE;
        }
        else if ([self isNumberMatchingDesc:nationalNumber numberDesc:metadata.mobile])
        {
            //NSLog(@"getNumberTypeHelper - FIXED_LINE_OR_MOBILE");
            return NBEPhoneNumberTypeFIXED_LINE_OR_MOBILE;
        }
        //NSLog(@"getNumberTypeHelper - FIXED_LINE");
        return NBEPhoneNumberTypeFIXED_LINE;
    }
    
    // Otherwise, test to see if the number is mobile. Only do this if certain
    // that the patterns for mobile and fixed line aren't the same.
    if ([metadata sameMobileAndFixedLinePattern] == NO && [self isNumberMatchingDesc:nationalNumber numberDesc:metadata.mobile]) {
        return NBEPhoneNumberTypeMOBILE;
    }
    
    return NBEPhoneNumberTypeUNKNOWN;
}


/**
 * @param {string} nationalNumber
 * @param {i18n.phonenumbers.PhoneNumberDesc} numberDesc
 * @return {boolean}
 * @private
 */
- (BOOL)isNumberMatchingDesc:(NSString *)nationalNumber numberDesc:(NBPhoneNumberDesc*)numberDesc
{
    if (numberDesc == nil) {
        return NO;
    }
    
    if ([NBMetadataHelper hasValue:numberDesc.possibleNumberPattern] == NO || [numberDesc.possibleNumberPattern isEqual:@"NA"]) {
        return [self matchesEntirely:numberDesc.nationalNumberPattern string:nationalNumber];
    }
    
    if ([NBMetadataHelper hasValue:numberDesc.nationalNumberPattern] == NO || [numberDesc.nationalNumberPattern isEqual:@"NA"]) {
        return [self matchesEntirely:numberDesc.possibleNumberPattern string:nationalNumber];
    }
    
    return [self matchesEntirely:numberDesc.possibleNumberPattern string:nationalNumber] &&
    [self matchesEntirely:numberDesc.nationalNumberPattern string:nationalNumber];
}


/**
 * Tests whether a phone number matches a valid pattern. Note this doesn't
 * verify the number is actually in use, which is impossible to tell by just
 * looking at a number itself.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want
 *     to validate.
 * @return {boolean} a boolean that indicates whether the number is of a valid
 *     pattern.
 */
- (BOOL)isValidNumber:(NBPhoneNumber*)number
{
    NSString *regionCode = [self getRegionCodeForNumber:number];
    return [self isValidNumberForRegion:number regionCode:regionCode];
}


/**
 * Tests whether a phone number is valid for a certain region. Note this doesn't
 * verify the number is actually in use, which is impossible to tell by just
 * looking at a number itself. If the country calling code is not the same as
 * the country calling code for the region, this immediately exits with NO.
 * After this, the specific number pattern rules for the region are examined.
 * This is useful for determining for example whether a particular number is
 * valid for Canada, rather than just a valid NANPA number.
 * Warning: In most cases, you want to use {@link #isValidNumber} instead. For
 * example, this method will mark numbers from British Crown dependencies such
 * as the Isle of Man as invalid for the region "GB" (United Kingdom), since it
 * has its own region code, "IM", which may be undesirable.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want
 *     to validate.
 * @param {?string} regionCode the region that we want to validate the phone
 *     number for.
 * @return {boolean} a boolean that indicates whether the number is of a valid
 *     pattern.
 */
- (BOOL)isValidNumberForRegion:(NBPhoneNumber*)number regionCode:(NSString *)regionCode
{
    NSNumber *countryCode = [number.countryCode copy];
    NBPhoneMetaData *metadata = [self getMetadataForRegionOrCallingCode:countryCode regionCode:regionCode];
    if (metadata == nil ||
        ([NB_REGION_CODE_FOR_NON_GEO_ENTITY isEqualToString:regionCode] == NO &&
         ![countryCode isEqualToNumber:[self getCountryCodeForValidRegion:regionCode error:nil]])) {
        // Either the region code was invalid, or the country calling code for this
        // number does not match that of the region code.
        return NO;
    }
    
    NBPhoneNumberDesc *generalNumDesc = metadata.generalDesc;
    NSString *nationalSignificantNumber = [self getNationalSignificantNumber:number];
    
    // For regions where we don't have metadata for PhoneNumberDesc, we treat any
    // number passed in as a valid number if its national significant number is
    // between the minimum and maximum lengths defined by ITU for a national
    // significant number.
    
    if ([NBMetadataHelper hasValue:generalNumDesc.nationalNumberPattern] == NO) {
        unsigned int numberLength = (unsigned int)nationalSignificantNumber.length;
        return numberLength > MIN_LENGTH_FOR_NSN_ && numberLength <= MAX_LENGTH_FOR_NSN_;
    }
    
    return [self getNumberTypeHelper:nationalSignificantNumber metadata:metadata] != NBEPhoneNumberTypeUNKNOWN;
}


/**
 * Returns the region where a phone number is from. This could be used for
 * geocoding at the region level.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone number whose origin
 *     we want to know.
 * @return {?string} the region where the phone number is from, or nil
 *     if no region matches this calling code.
 */
- (NSString *)getRegionCodeForNumber:(NBPhoneNumber*)phoneNumber
{
    if (phoneNumber == nil) {
        return nil;
    }
    
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NSArray *regionCodes = [helper regionCodeFromCountryCode:phoneNumber.countryCode];
    if (regionCodes == nil || [regionCodes count] <= 0) {
        return nil;
    }
    
    if ([regionCodes count] == 1) {
        return [regionCodes objectAtIndex:0];
    } else {
        return [self getRegionCodeForNumberFromRegionList:phoneNumber regionCodes:regionCodes];
    }
}


/**
 * @param {i18n.phonenumbers.PhoneNumber} number
 * @param {Array.<string>} regionCodes
 * @return {?string}
 * @private
 
 */
- (NSString *)getRegionCodeForNumberFromRegionList:(NBPhoneNumber*)phoneNumber regionCodes:(NSArray*)regionCodes
{
    NSString *nationalNumber = [self getNationalSignificantNumber:phoneNumber];
    unsigned int regionCodesCount = (unsigned int)[regionCodes count];
    
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    
    for (unsigned int i = 0; i<regionCodesCount; i++) {
        NSString *regionCode = [regionCodes objectAtIndex:i];
        NBPhoneMetaData *metadata = [helper getMetadataForRegion:regionCode];
        
        if ([NBMetadataHelper hasValue:metadata.leadingDigits]) {
            if ([self stringPositionByRegex:nationalNumber regex:metadata.leadingDigits] == 0) {
                return regionCode;
            }
        } else if ([self getNumberTypeHelper:nationalNumber metadata:metadata] != NBEPhoneNumberTypeUNKNOWN) {
            return regionCode;
        }
    }
    
    return nil;
}


/**
 * Returns the region code that matches the specific country calling code. In
 * the case of no region code being found, ZZ will be returned. In the case of
 * multiple regions, the one designated in the metadata as the 'main' region for
 * this calling code will be returned.
 *
 * @param {number} countryCallingCode the country calling code.
 * @return {string}
 */
- (NSString *)getRegionCodeForCountryCode:(NSNumber *)countryCallingCode
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NSArray *regionCodes = [helper regionCodeFromCountryCode:countryCallingCode];
    return regionCodes == nil ? UNKNOWN_REGION_ : [regionCodes objectAtIndex:0];
}


/**
 * Returns a list with the region codes that match the specific country calling
 * code. For non-geographical country calling codes, the region code 001 is
 * returned. Also, in the case of no region code being found, an empty list is
 * returned.
 *
 * @param {number} countryCallingCode the country calling code.
 * @return {Array.<string>}
 */
- (NSArray*)getRegionCodesForCountryCode:(NSNumber *)countryCallingCode
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NSArray *regionCodes = [helper regionCodeFromCountryCode:countryCallingCode];
    return regionCodes == nil ? nil : regionCodes;
}


/**
 * Returns the country calling code for a specific region. For example, this
 * would be 1 for the United States, and 64 for New Zealand.
 *
 * @param {?string} regionCode the region that we want to get the country
 *     calling code for.
 * @return {number} the country calling code for the region denoted by
 *     regionCode.
 */
- (NSNumber*)getCountryCodeForRegion:(NSString *)regionCode
{
    if ([self isValidRegionCode:regionCode] == NO) {
        return @0;
    }
    
    NSError *error = nil;
    NSNumber *res = [self getCountryCodeForValidRegion:regionCode error:&error];
    if (error != nil) {
        return @0;
    }
    
    return res;
}


/**
 * Returns the country calling code for a specific region. For example, this
 * would be 1 for the United States, and 64 for New Zealand. Assumes the region
 * is already valid.
 *
 * @param {?string} regionCode the region that we want to get the country
 *     calling code for.
 * @return {number} the country calling code for the region denoted by
 *     regionCode.
 * @throws {string} if the region is invalid
 * @private
 */
- (NSNumber*)getCountryCodeForValidRegion:(NSString *)regionCode error:(NSError**)error
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NBPhoneMetaData *metadata = [helper getMetadataForRegion:regionCode];
    
    if (metadata == nil) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid region code:%@", regionCode]
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL) {
            (*error) = [NSError errorWithDomain:@"INVALID_REGION_CODE" code:0 userInfo:userInfo];
        }
        
        return @-1;
    }
    
    return metadata.countryCode;
}


/**
 * Returns the national dialling prefix for a specific region. For example, this
 * would be 1 for the United States, and 0 for New Zealand. Set stripNonDigits
 * to NO to strip symbols like '~' (which indicates a wait for a dialling
 * tone) from the prefix returned. If no national prefix is present, we return
 * nil.
 *
 * <p>Warning: Do not use this method for do-your-own formatting - for some
 * regions, the national dialling prefix is used only for certain types of
 * numbers. Use the library's formatting functions to prefix the national prefix
 * when required.
 *
 * @param {?string} regionCode the region that we want to get the dialling
 *     prefix for.
 * @param {boolean} stripNonDigits NO to strip non-digits from the national
 *     dialling prefix.
 * @return {?string} the dialling prefix for the region denoted by
 *     regionCode.
 */
- (NSString *)getNddPrefixForRegion:(NSString *)regionCode stripNonDigits:(BOOL)stripNonDigits
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NBPhoneMetaData *metadata = [helper getMetadataForRegion:regionCode];
    if (metadata == nil) {
        return nil;
    }
    
    NSString *nationalPrefix = metadata.nationalPrefix;
    // If no national prefix was found, we return nil.
    if (nationalPrefix.length == 0) {
        return nil;
    }
    
    if (stripNonDigits) {
        // Note: if any other non-numeric symbols are ever used in national
        // prefixes, these would have to be removed here as well.
        nationalPrefix = [nationalPrefix stringByReplacingOccurrencesOfString:@"~" withString:@""];
    }
    return nationalPrefix;
}


/**
 * Checks if this is a region under the North American Numbering Plan
 * Administration (NANPA).
 *
 * @param {?string} regionCode the ISO 3166-1 two-letter region code.
 * @return {boolean} NO if regionCode is one of the regions under NANPA.
 */
- (BOOL)isNANPACountry:(NSString *)regionCode
{
    BOOL isExists = NO;
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NSArray *res = [helper regionCodeFromCountryCode:[NSNumber numberWithUnsignedInteger:NANPA_COUNTRY_CODE_]];
    
    for (NSString *inRegionCode in res) {
        if ([inRegionCode isEqualToString:regionCode.uppercaseString]) {
            isExists = YES;
        }
    }
    
    return regionCode != nil && isExists;
}


/**
 * Checks whether countryCode represents the country calling code from a region
 * whose national significant number could contain a leading zero. An example of
 * such a region is Italy. Returns NO if no metadata for the country is
 * found.
 *
 * @param {number} countryCallingCode the country calling code.
 * @return {boolean}
 */
- (BOOL)isLeadingZeroPossible:(NSNumber *)countryCallingCode
{
    NBPhoneMetaData *mainMetadataForCallingCode = [self getMetadataForRegionOrCallingCode:countryCallingCode
                                                                               regionCode:[self getRegionCodeForCountryCode:countryCallingCode]];
    
    return mainMetadataForCallingCode != nil && mainMetadataForCallingCode.leadingZeroPossible;
}


/**
 * Checks if the number is a valid vanity (alpha) number such as 800 MICROSOFT.
 * A valid vanity number will start with at least 3 digits and will have three
 * or more alpha characters. This does not do region-specific checks - to work
 * out if this number is actually valid for a region, it should be parsed and
 * methods such as {@link #isPossibleNumberWithReason} and
 * {@link #isValidNumber} should be used.
 *
 * @param {string} number the number that needs to be checked.
 * @return {boolean} NO if the number is a valid vanity number.
 */
- (BOOL)isAlphaNumber:(NSString *)number
{
    if ([self isViablePhoneNumber:number] == NO) {
        // Number is too short, or doesn't match the basic phone number pattern.
        return NO;
    }
    
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    number = [helper normalizeNonBreakingSpace:number];
    
    /** @type {!goog.string.StringBuffer} */
    NSString *strippedNumber = [number copy];
    [self maybeStripExtension:&strippedNumber];
    
    return [self matchesEntirely:VALID_ALPHA_PHONE_PATTERN_STRING string:strippedNumber];
}


/**
 * Convenience wrapper around {@link #isPossibleNumberWithReason}. Instead of
 * returning the reason for failure, this method returns a boolean value.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the number that needs to be
 *     checked.
 * @return {boolean} NO if the number is possible.
 */
- (BOOL)isPossibleNumber:(NBPhoneNumber*)number error:(NSError**)error
{
    BOOL res = NO;
    @try {
        res = [self isPossibleNumber:number];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    return res;
}


- (BOOL)isPossibleNumber:(NBPhoneNumber*)number
{
    return [self isPossibleNumberWithReason:number] == NBEValidationResultIS_POSSIBLE;
}


/**
 * Helper method to check a number against a particular pattern and determine
 * whether it matches, or is too short or too long. Currently, if a number
 * pattern suggests that numbers of length 7 and 10 are possible, and a number
 * in between these possible lengths is entered, such as of length 8, this will
 * return TOO_LONG.
 *
 * @param {string} numberPattern
 * @param {string} number
 * @return {ValidationResult}
 * @private
 */
- (NBEValidationResult)testNumberLengthAgainstPattern:(NSString *)numberPattern number:(NSString *)number
{
    if ([self matchesEntirely:numberPattern string:number]) {
        return NBEValidationResultIS_POSSIBLE;
    }
    
    if ([self stringPositionByRegex:number regex:numberPattern] == 0) {
        return NBEValidationResultTOO_LONG;
    } else {
        return NBEValidationResultTOO_SHORT;
    }
}


/**
 * Check whether a phone number is a possible number. It provides a more lenient
 * check than {@link #isValidNumber} in the following sense:
 * <ol>
 * <li>It only checks the length of phone numbers. In particular, it doesn't
 * check starting digits of the number.
 * <li>It doesn't attempt to figure out the type of the number, but uses general
 * rules which applies to all types of phone numbers in a region. Therefore, it
 * is much faster than isValidNumber.
 * <li>For fixed line numbers, many regions have the concept of area code, which
 * together with subscriber number constitute the national significant number.
 * It is sometimes okay to dial the subscriber number only when dialing in the
 * same area. This function will return NO if the subscriber-number-only
 * version is passed in. On the other hand, because isValidNumber validates
 * using information on both starting digits (for fixed line numbers, that would
 * most likely be area codes) and length (obviously includes the length of area
 * codes for fixed line numbers), it will return NO for the
 * subscriber-number-only version.
 * </ol>
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the number that needs to be
 *     checked.
 * @return {ValidationResult} a
 *     ValidationResult object which indicates whether the number is possible.
 */
- (NBEValidationResult)isPossibleNumberWithReason:(NBPhoneNumber*)number error:(NSError *__autoreleasing *)error
{
    NBEValidationResult res = NBEValidationResultUNKNOWN;
    @try {
        res = [self isPossibleNumberWithReason:number];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    
    return res;
}


- (NBEValidationResult)isPossibleNumberWithReason:(NBPhoneNumber*)number
{
    NSString *nationalNumber = [self getNationalSignificantNumber:number];
    NSNumber *countryCode = number.countryCode;
    // Note: For Russian Fed and NANPA numbers, we just use the rules from the
    // default region (US or Russia) since the getRegionCodeForNumber will not
    // work if the number is possible but not valid. This would need to be
    // revisited if the possible number pattern ever differed between various
    // regions within those plans.
    if ([self hasValidCountryCallingCode:countryCode] == NO) {
        return NBEValidationResultINVALID_COUNTRY_CODE;
    }
    
    NSString *regionCode = [self getRegionCodeForCountryCode:countryCode];
    // Metadata cannot be nil because the country calling code is valid.
    NBPhoneMetaData *metadata = [self getMetadataForRegionOrCallingCode:countryCode regionCode:regionCode];
    NBPhoneNumberDesc *generalNumDesc = metadata.generalDesc;
    
    // Handling case of numbers with no metadata.
    if ([NBMetadataHelper hasValue:generalNumDesc.nationalNumberPattern] == NO) {
        unsigned int numberLength = (unsigned int)nationalNumber.length;
        
        if (numberLength < MIN_LENGTH_FOR_NSN_) {
            return NBEValidationResultTOO_SHORT;
        } else if (numberLength > MAX_LENGTH_FOR_NSN_) {
            return NBEValidationResultTOO_LONG;
        } else {
            return NBEValidationResultIS_POSSIBLE;
        }
    }
    
    NSString *possibleNumberPattern = generalNumDesc.possibleNumberPattern;
    return [self testNumberLengthAgainstPattern:possibleNumberPattern number:nationalNumber];
}


/**
 * Check whether a phone number is a possible number given a number in the form
 * of a string, and the region where the number could be dialed from. It
 * provides a more lenient check than {@link #isValidNumber}. See
 * {@link #isPossibleNumber} for details.
 *
 * <p>This method first parses the number, then invokes
 * {@link #isPossibleNumber} with the resultant PhoneNumber object.
 *
 * @param {string} number the number that needs to be checked, in the form of a
 *     string.
 * @param {string} regionDialingFrom the region that we are expecting the number
 *     to be dialed from.
 *     Note this is different from the region where the number belongs.
 *     For example, the number +1 650 253 0000 is a number that belongs to US.
 *     When written in this form, it can be dialed from any region. When it is
 *     written as 00 1 650 253 0000, it can be dialed from any region which uses
 *     an international dialling prefix of 00. When it is written as
 *     650 253 0000, it can only be dialed from within the US, and when written
 *     as 253 0000, it can only be dialed from within a smaller area in the US
 *     (Mountain View, CA, to be more specific).
 * @return {boolean} NO if the number is possible.
 */
- (BOOL)isPossibleNumberString:(NSString *)number regionDialingFrom:(NSString *)regionDialingFrom error:(NSError**)error
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    number = [helper normalizeNonBreakingSpace:number];
    
    BOOL res = [self isPossibleNumber:[self parse:number defaultRegion:regionDialingFrom error:error]];
    return res;
}

/**
 * Attempts to extract a valid number from a phone number that is too long to be
 * valid, and resets the PhoneNumber object passed in to that valid version. If
 * no valid number could be extracted, the PhoneNumber object passed in will not
 * be modified.
 * @param {i18n.phonenumbers.PhoneNumber} number a PhoneNumber object which
 *     contains a number that is too long to be valid.
 * @return {boolean} NO if a valid phone number can be successfully extracted.
 */

- (BOOL)truncateTooLongNumber:(NBPhoneNumber*)number
{
    if ([self isValidNumber:number]) {
        return YES;
    }
    
    NBPhoneNumber *numberCopy = [number copy];
    NSNumber *nationalNumber = number.nationalNumber;
    do {
        nationalNumber = [NSNumber numberWithLongLong:(long long)floor(nationalNumber.unsignedLongLongValue / 10)];
        numberCopy.nationalNumber = [nationalNumber copy];
        if ([nationalNumber isEqualToNumber:@0] || [self isPossibleNumberWithReason:numberCopy] == NBEValidationResultTOO_SHORT) {
            return NO;
        }
    }
    while ([self isValidNumber:numberCopy] == NO);
    
    number.nationalNumber = nationalNumber;
    return YES;
}


/**
 * Extracts country calling code from fullNumber, returns it and places the
 * remaining number in nationalNumber. It assumes that the leading plus sign or
 * IDD has already been removed. Returns 0 if fullNumber doesn't start with a
 * valid country calling code, and leaves nationalNumber unmodified.
 *
 * @param {!goog.string.StringBuffer} fullNumber
 * @param {!goog.string.StringBuffer} nationalNumber
 * @return {number}
 */
- (NSNumber *)extractCountryCode:(NSString *)fullNumber nationalNumber:(NSString **)nationalNumber
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    fullNumber = [helper normalizeNonBreakingSpace:fullNumber];
    
    if ((fullNumber.length == 0) || ([[fullNumber substringToIndex:1] isEqualToString:@"0"])) {
        // Country codes do not begin with a '0'.
        return @0;
    }
    
    unsigned int numberLength = (unsigned int)fullNumber.length;
    unsigned int maxCountryCode = MAX_LENGTH_COUNTRY_CODE_;
    
    if ([fullNumber hasPrefix:@"+"]) {
        maxCountryCode = MAX_LENGTH_COUNTRY_CODE_ + 1;
    }
    
    for (unsigned int i = 1; i <= maxCountryCode && i <= numberLength; ++i) {
        NSString *subNumber = [fullNumber substringWithRange:NSMakeRange(0, i)];
        NSNumber *potentialCountryCode = [NSNumber numberWithInteger:[subNumber integerValue]];
        
        NSArray *regionCodes = [helper regionCodeFromCountryCode:potentialCountryCode];
        if (regionCodes != nil && regionCodes.count > 0) {
            if (nationalNumber != NULL) {
                if ((*nationalNumber) == nil) {
                    (*nationalNumber) = [NSString stringWithFormat:@"%@", [fullNumber substringFromIndex:i]];
                } else {
                    (*nationalNumber) = [NSString stringWithFormat:@"%@%@", (*nationalNumber), [fullNumber substringFromIndex:i]];
                }
            }
            return potentialCountryCode;
        }
    }
    
    return @0;
}

//todo:

 /**
 * Convenience method to get a list of what regions the library has metadata
 * for.
 * @return {!Array.<string>} region codes supported by the library.
 */
/*
i18n.phonenumbers.PhoneNumberUtil.prototype.getSupportedRegions = function() {
    return goog.array.filter(
                             Object.keys(i18n.phonenumbers.metadata.countryToMetadata),
                             function(regionCode) {
                                 return isNaN(regionCode);
                             });
};
*/

/**
 * Convenience method to get a list of what global network calling codes the
 * library has metadata for.
 * @return {!Array.<number>} global network calling codes supported by the
 *     library.
 */
/*
i18n.phonenumbers.PhoneNumberUtil.prototype.
getSupportedGlobalNetworkCallingCodes = function() {
    var callingCodesAsStrings = goog.array.filter(
                                                  Object.keys(i18n.phonenumbers.metadata.countryToMetadata),
                                                  function(regionCode) {
                                                      return !isNaN(regionCode);
                                                  });
    return goog.array.map(callingCodesAsStrings,
                          function(callingCode) {
                              return parseInt(callingCode, 10);
                          });
};
*/


/**
 * Tries to extract a country calling code from a number. This method will
 * return zero if no country calling code is considered to be present. Country
 * calling codes are extracted in the following ways:
 * <ul>
 * <li>by stripping the international dialing prefix of the region the person is
 * dialing from, if this is present in the number, and looking at the next
 * digits
 * <li>by stripping the '+' sign if present and then looking at the next digits
 * <li>by comparing the start of the number and the country calling code of the
 * default region. If the number is not considered possible for the numbering
 * plan of the default region initially, but starts with the country calling
 * code of this region, validation will be reattempted after stripping this
 * country calling code. If this number is considered a possible number, then
 * the first digits will be considered the country calling code and removed as
 * such.
 * </ul>
 *
 * It will throw a i18n.phonenumbers.Error if the number starts with a '+' but
 * the country calling code supplied after this does not match that of any known
 * region.
 *
 * @param {string} number non-normalized telephone number that we wish to
 *     extract a country calling code from - may begin with '+'.
 * @param {i18n.phonenumbers.PhoneMetadata} defaultRegionMetadata metadata
 *     about the region this number may be from.
 * @param {!goog.string.StringBuffer} nationalNumber a string buffer to store
 *     the national significant number in, in the case that a country calling
 *     code was extracted. The number is appended to any existing contents. If
 *     no country calling code was extracted, this will be left unchanged.
 * @param {boolean} keepRawInput NO if the country_code_source and
 *     preferred_carrier_code fields of phoneNumber should be populated.
 * @param {i18n.phonenumbers.PhoneNumber} phoneNumber the PhoneNumber object
 *     where the country_code and country_code_source need to be populated.
 *     Note the country_code is always populated, whereas country_code_source is
 *     only populated when keepCountryCodeSource is NO.
 * @return {number} the country calling code extracted or 0 if none could be
 *     extracted.
 * @throws {i18n.phonenumbers.Error}
 */
- (NSNumber *)maybeExtractCountryCode:(NSString *)number metadata:(NBPhoneMetaData*)defaultRegionMetadata
                   nationalNumber:(NSString **)nationalNumber keepRawInput:(BOOL)keepRawInput
                      phoneNumber:(NBPhoneNumber**)phoneNumber error:(NSError**)error
{
    if (nationalNumber == NULL || phoneNumber == NULL || number.length <= 0) {
        return @0;
    }
    
    NSString *fullNumber = [number copy];
    // Set the default prefix to be something that will never match.
    NSString *possibleCountryIddPrefix = @"";
    if (defaultRegionMetadata != nil) {
        possibleCountryIddPrefix = defaultRegionMetadata.internationalPrefix;
    }
    
    if (possibleCountryIddPrefix == nil) {
        possibleCountryIddPrefix = @"NonMatch";
    }
    
    /** @type {i18n.phonenumbers.PhoneNumber.CountryCodeSource} */
    NBECountryCodeSource countryCodeSource = [self maybeStripInternationalPrefixAndNormalize:&fullNumber
                                                                           possibleIddPrefix:possibleCountryIddPrefix];
    if (keepRawInput) {
        (*phoneNumber).countryCodeSource = [NSNumber numberWithInt:countryCodeSource];
    }
    
    if (countryCodeSource != NBECountryCodeSourceFROM_DEFAULT_COUNTRY) {
        if (fullNumber.length <= MIN_LENGTH_FOR_NSN_) {
            NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"TOO_SHORT_AFTER_IDD:%@", fullNumber]
                                                                 forKey:NSLocalizedDescriptionKey];
            if (error != NULL) {
                (*error) = [NSError errorWithDomain:@"TOO_SHORT_AFTER_IDD" code:0 userInfo:userInfo];
            }
            return @0;
        }
        
        NSNumber *potentialCountryCode = [self extractCountryCode:fullNumber nationalNumber:nationalNumber];
        
        if (![potentialCountryCode isEqualToNumber:@0]) {
            (*phoneNumber).countryCode = potentialCountryCode;
            return potentialCountryCode;
        }
        
        // If this fails, they must be using a strange country calling code that we
        // don't recognize, or that doesn't exist.
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"INVALID_COUNTRY_CODE:%@", potentialCountryCode]
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL) {
            (*error) = [NSError errorWithDomain:@"INVALID_COUNTRY_CODE" code:0 userInfo:userInfo];
        }
        
        return @0;
    } else if (defaultRegionMetadata != nil) {
        // Check to see if the number starts with the country calling code for the
        // default region. If so, we remove the country calling code, and do some
        // checks on the validity of the number before and after.
        NSNumber *defaultCountryCode = defaultRegionMetadata.countryCode;
        NSString *defaultCountryCodeString = [NSString stringWithFormat:@"%@", defaultCountryCode];
        NSString *normalizedNumber = [fullNumber copy];
        
        if ([normalizedNumber hasPrefix:defaultCountryCodeString]) {
            NSString *potentialNationalNumber = [normalizedNumber substringFromIndex:defaultCountryCodeString.length];
            NBPhoneNumberDesc *generalDesc = defaultRegionMetadata.generalDesc;
            
            NSString *validNumberPattern = generalDesc.nationalNumberPattern;
            // Passing null since we don't need the carrier code.
            [self maybeStripNationalPrefixAndCarrierCode:&potentialNationalNumber metadata:defaultRegionMetadata carrierCode:nil];
            
            NSString *potentialNationalNumberStr = [potentialNationalNumber copy];
            NSString *possibleNumberPattern = generalDesc.possibleNumberPattern;
            // If the number was not valid before but is valid now, or if it was too
            // long before, we consider the number with the country calling code
            // stripped to be a better result and keep that instead.
            if ((![self matchesEntirely:validNumberPattern string:fullNumber] &&
                 [self matchesEntirely:validNumberPattern string:potentialNationalNumberStr]) ||
                [self testNumberLengthAgainstPattern:possibleNumberPattern number:fullNumber] == NBEValidationResultTOO_LONG) {
                (*nationalNumber) = [(*nationalNumber) stringByAppendingString:potentialNationalNumberStr];
                if (keepRawInput) {
                    (*phoneNumber).countryCodeSource = [NSNumber numberWithInt:NBECountryCodeSourceFROM_NUMBER_WITHOUT_PLUS_SIGN];
                }
                (*phoneNumber).countryCode = defaultCountryCode;
                return defaultCountryCode;
            }
        }
    }
    // No country calling code present.
    (*phoneNumber).countryCode = @0;
    return @0;
}


/**
 * Strips the IDD from the start of the number if present. Helper function used
 * by maybeStripInternationalPrefixAndNormalize.
 *
 * @param {!RegExp} iddPattern the regular expression for the international
 *     prefix.
 * @param {!goog.string.StringBuffer} number the phone number that we wish to
 *     strip any international dialing prefix from.
 * @return {boolean} NO if an international prefix was present.
 * @private
 */
- (BOOL)parsePrefixAsIdd:(NSString *)iddPattern sourceString:(NSString **)number
{
    if (number == NULL) {
        return NO;
    }
    
    NSString *numberStr = [(*number) copy];
    
    if ([self stringPositionByRegex:numberStr regex:iddPattern] == 0) {
        NSTextCheckingResult *matched = [[self matchesByRegex:numberStr regex:iddPattern] objectAtIndex:0];
        NSString *matchedString = [numberStr substringWithRange:matched.range];
        unsigned int matchEnd = (unsigned int)matchedString.length;
        NSString *remainString = [numberStr substringFromIndex:matchEnd];
        
        NSRegularExpression *currentPattern = CAPTURING_DIGIT_PATTERN;
        NSArray *matchedGroups = [currentPattern matchesInString:remainString options:0 range:NSMakeRange(0, remainString.length)];
        
        if (matchedGroups && [matchedGroups count] > 0 && [matchedGroups objectAtIndex:0] != nil) {
            NSString *digitMatched = [remainString substringWithRange:((NSTextCheckingResult*)[matchedGroups objectAtIndex:0]).range];
            if (digitMatched.length > 0) {
                NSString *normalizedGroup = [self normalizeDigitsOnly:digitMatched];
                if ([normalizedGroup isEqualToString:@"0"]) {
                    return NO;
                }
            }
        }
        
        (*number) = [remainString copy];
        return YES;
    }
    
    return NO;
}


/**
 * Strips any international prefix (such as +, 00, 011) present in the number
 * provided, normalizes the resulting number, and indicates if an international
 * prefix was present.
 *
 * @param {!goog.string.StringBuffer} number the non-normalized telephone number
 *     that we wish to strip any international dialing prefix from.
 * @param {string} possibleIddPrefix the international direct dialing prefix
 *     from the region we think this number may be dialed in.
 * @return {CountryCodeSource} the corresponding
 *     CountryCodeSource if an international dialing prefix could be removed
 *     from the number, otherwise CountryCodeSource.FROM_DEFAULT_COUNTRY if
 *     the number did not seem to be in international format.
 */
- (NBECountryCodeSource)maybeStripInternationalPrefixAndNormalize:(NSString **)numberStr possibleIddPrefix:(NSString *)possibleIddPrefix
{
    if (numberStr == NULL || (*numberStr).length == 0) {
        return NBECountryCodeSourceFROM_DEFAULT_COUNTRY;
    }
    
    // Check to see if the number begins with one or more plus signs.
    if ([self isStartingStringByRegex:(*numberStr) regex:LEADING_PLUS_CHARS_PATTERN]) {
        (*numberStr) = [self replaceStringByRegex:(*numberStr) regex:LEADING_PLUS_CHARS_PATTERN withTemplate:@""];
        // Can now normalize the rest of the number since we've consumed the '+'
        // sign at the start.
        (*numberStr) = [self normalizePhoneNumber:(*numberStr)];
        return NBECountryCodeSourceFROM_NUMBER_WITH_PLUS_SIGN;
    }
    
    // Attempt to parse the first digits as an international prefix.
    NSString *iddPattern = [possibleIddPrefix copy];
    [self normalizeSB:numberStr];
    
    return [self parsePrefixAsIdd:iddPattern sourceString:numberStr] ? NBECountryCodeSourceFROM_NUMBER_WITH_IDD : NBECountryCodeSourceFROM_DEFAULT_COUNTRY;
}


/**
 * Strips any national prefix (such as 0, 1) present in the number provided.
 *
 * @param {!goog.string.StringBuffer} number the normalized telephone number
 *     that we wish to strip any national dialing prefix from.
 * @param {i18n.phonenumbers.PhoneMetadata} metadata the metadata for the
 *     region that we think this number is from.
 * @param {goog.string.StringBuffer} carrierCode a place to insert the carrier
 *     code if one is extracted.
 * @return {boolean} NO if a national prefix or carrier code (or both) could
 *     be extracted.
 */
- (BOOL)maybeStripNationalPrefixAndCarrierCode:(NSString **)number metadata:(NBPhoneMetaData*)metadata carrierCode:(NSString **)carrierCode
{
    if (number == NULL) {
        return NO;
    }
    
    NSString *numberStr = [(*number) copy];
    unsigned int numberLength = (unsigned int)numberStr.length;
    NSString *possibleNationalPrefix = metadata.nationalPrefixForParsing;
    
    if (numberLength == 0 || [NBMetadataHelper hasValue:possibleNationalPrefix] == NO) {
        // Early return for numbers of zero length.
        return NO;
    }
    
    // Attempt to parse the first digits as a national prefix.
    NSString *prefixPattern = [NSString stringWithFormat:@"^(?:%@)", possibleNationalPrefix];
    NSError *error = nil;
    NSRegularExpression *currentPattern = [self regularExpressionWithPattern:prefixPattern options:0 error:&error];
    
    NSArray *prefixMatcher = [currentPattern matchesInString:numberStr options:0 range:NSMakeRange(0, numberLength)];
    if (prefixMatcher && [prefixMatcher count] > 0) {
        NSString *nationalNumberRule = metadata.generalDesc.nationalNumberPattern;
        NSTextCheckingResult *firstMatch = [prefixMatcher objectAtIndex:0];
        NSString *firstMatchString = [numberStr substringWithRange:firstMatch.range];
        
        // prefixMatcher[numOfGroups] == null implies nothing was captured by the
        // capturing groups in possibleNationalPrefix; therefore, no transformation
        // is necessary, and we just remove the national prefix.
        unsigned int numOfGroups = (unsigned int)firstMatch.numberOfRanges - 1;
        NSString *transformRule = metadata.nationalPrefixTransformRule;
        NSString *transformedNumber = @"";
        NSRange firstRange = [firstMatch rangeAtIndex:numOfGroups];
        NSString *firstMatchStringWithGroup = (firstRange.location != NSNotFound && firstRange.location < numberStr.length) ? [numberStr substringWithRange:firstRange] : nil;
        BOOL noTransform = (transformRule == nil || transformRule.length == 0 || [NBMetadataHelper hasValue:firstMatchStringWithGroup] == NO);
        
        if (noTransform) {
            transformedNumber = [numberStr substringFromIndex:firstMatchString.length];
        } else {
            transformedNumber = [self replaceFirstStringByRegex:numberStr regex:prefixPattern withTemplate:transformRule];
        }
        // If the original number was viable, and the resultant number is not,
        // we return.
        if ([NBMetadataHelper hasValue:nationalNumberRule ] && [self matchesEntirely:nationalNumberRule string:numberStr] &&
            [self matchesEntirely:nationalNumberRule string:transformedNumber] == NO) {
            return NO;
        }
        
        if ((noTransform && numOfGroups > 0 && [NBMetadataHelper hasValue:firstMatchStringWithGroup]) || (!noTransform && numOfGroups > 1)) {
            if (carrierCode != NULL && (*carrierCode) != nil) {
                (*carrierCode) = [(*carrierCode) stringByAppendingString:firstMatchStringWithGroup];
            }
        } else if ((noTransform && numOfGroups > 0 && [NBMetadataHelper hasValue:firstMatchString]) || (!noTransform && numOfGroups > 1)) {
            if (carrierCode != NULL && (*carrierCode) != nil) {
                (*carrierCode) = [(*carrierCode) stringByAppendingString:firstMatchString];
            }
        }
        
        (*number) = transformedNumber;
        return YES;
    }
    return NO;
}


/**
 * Strips any extension (as in, the part of the number dialled after the call is
 * connected, usually indicated with extn, ext, x or similar) from the end of
 * the number, and returns it.
 *
 * @param {!goog.string.StringBuffer} number the non-normalized telephone number
 *     that we wish to strip the extension from.
 * @return {string} the phone extension.
 */
- (NSString *)maybeStripExtension:(NSString **)number
{
    if (number == NULL) {
        return @"";
    }
    
    NSString *numberStr = [(*number) copy];
    int mStart = [self stringPositionByRegex:numberStr regex:EXTN_PATTERN];
    
    // If we find a potential extension, and the number preceding this is a viable
    // number, we assume it is an extension.
    if (mStart >= 0 && [self isViablePhoneNumber:[numberStr substringWithRange:NSMakeRange(0, mStart)]]) {
        // The numbers are captured into groups in the regular expression.
        NSTextCheckingResult *firstMatch = [self matcheFirstByRegex:numberStr regex:EXTN_PATTERN];
        unsigned int matchedGroupsLength = (unsigned int)[firstMatch numberOfRanges];
        
        for (unsigned int i=1; i<matchedGroupsLength; i++) {
            NSRange curRange = [firstMatch rangeAtIndex:i];
            
            if (curRange.location != NSNotFound && curRange.location < numberStr.length) {
                NSString *matchString = [(*number) substringWithRange:curRange];
                // We go through the capturing groups until we find one that captured
                // some digits. If none did, then we will return the empty string.
                NSString *tokenedString = [numberStr substringWithRange:NSMakeRange(0, mStart)];
                (*number) = @"";
                (*number) = [(*number) stringByAppendingString:tokenedString];
                
                return matchString;
            }
        }
    }
    
    return @"";
}


/**
 * Checks to see that the region code used is valid, or if it is not valid, that
 * the number to parse starts with a + symbol so that we can attempt to infer
 * the region from the number.
 * @param {string} numberToParse number that we are attempting to parse.
 * @param {?string} defaultRegion region that we are expecting the number to be
 *     from.
 * @return {boolean} NO if it cannot use the region provided and the region
 *     cannot be inferred.
 * @private
 */
- (BOOL)checkRegionForParsing:(NSString *)numberToParse defaultRegion:(NSString *)defaultRegion
{
    // If the number is nil or empty, we can't infer the region.
    return [self isValidRegionCode:defaultRegion] ||
    (numberToParse != nil && numberToParse.length > 0 && [self isStartingStringByRegex:numberToParse regex:LEADING_PLUS_CHARS_PATTERN]);
}


/**
 * Parses a string and returns it in proto buffer format. This method will throw
 * a {@link i18n.phonenumbers.Error} if the number is not considered to be a
 * possible number. Note that validation of whether the number is actually a
 * valid number for a particular region is not performed. This can be done
 * separately with {@link #isValidNumber}.
 *
 * @param {?string} numberToParse number that we are attempting to parse. This
 *     can contain formatting such as +, ( and -, as well as a phone number
 *     extension. It can also be provided in RFC3966 format.
 * @param {?string} defaultRegion region that we are expecting the number to be
 *     from. This is only used if the number being parsed is not written in
 *     international format. The country_code for the number in this case would
 *     be stored as that of the default region supplied. If the number is
 *     guaranteed to start with a '+' followed by the country calling code, then
 *     'ZZ' or nil can be supplied.
 * @return {i18n.phonenumbers.PhoneNumber} a phone number proto buffer filled
 *     with the parsed number.
 * @throws {i18n.phonenumbers.Error} if the string is not considered to be a
 *     viable phone number or if no default region was supplied and the number
 *     is not in international format (does not start with +).
 */
- (NBPhoneNumber*)parse:(NSString *)numberToParse defaultRegion:(NSString *)defaultRegion error:(NSError**)error
{
    NSError *anError = nil;
    NBPhoneNumber *phoneNumber = [self parseHelper:numberToParse defaultRegion:defaultRegion keepRawInput:NO checkRegion:YES error:&anError];
    
    if (anError != nil) {
        if (error != NULL) {
            (*error) = [self errorWithObject:anError.description withDomain:anError.domain];
        }
    }
    return phoneNumber;
}

/**
 * Parses a string using the phone's carrier region (when available, ZZ otherwise).
 * This uses the country the sim card in the phone is registered with.
 * For example if you have an AT&T sim card but are in Europe, this will parse the
 * number using +1 (AT&T is a US Carrier) as the default country code.
 * This also works for CDMA phones which don't have a sim card.
 */
- (NBPhoneNumber*)parseWithPhoneCarrierRegion:(NSString *)numberToParse error:(NSError**)error
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    
    numberToParse = [helper normalizeNonBreakingSpace:numberToParse];
    
    NSString *defaultRegion = nil;
#if TARGET_OS_IPHONE
    defaultRegion = [self countryCodeByCarrier];
#else
    defaultRegion = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode];
#endif
    if ([UNKNOWN_REGION_ isEqualToString:defaultRegion]) {
        // get region from device as a failover (e.g. iPad)
        NSLocale *currentLocale = [NSLocale currentLocale];
        defaultRegion = [currentLocale objectForKey:NSLocaleCountryCode];
    }
    
    return [self parse:numberToParse defaultRegion:defaultRegion error:error];
}

#if TARGET_OS_IPHONE

- (NSString *)countryCodeByCarrier
{
    // cache telephony network info;
    // CTTelephonyNetworkInfo objects are unnecessarily created for every call to parseWithPhoneCarrierRegion:error:
    // when in reality this information not change while an app lives in memory
    // real-world performance test while parsing 93 phone numbers:
    // before change:   126ms
    // after change:    32ms
    if (!self.telephonyNetworkInfo) {
        self.telephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init];
    }
    
    NSString *isoCode = [[self.telephonyNetworkInfo subscriberCellularProvider] isoCountryCode];
    
    // The 2nd part of the if is working around an iOS 7 bug
    // If the SIM card is missing, iOS 7 returns an empty string instead of nil
    if (!isoCode || [isoCode isEqualToString:@""]) {
        isoCode = UNKNOWN_REGION_;
    }
    
    return isoCode;
}

#endif


/**
 * Parses a string and returns it in proto buffer format. This method differs
 * from {@link #parse} in that it always populates the raw_input field of the
 * protocol buffer with numberToParse as well as the country_code_source field.
 *
 * @param {string} numberToParse number that we are attempting to parse. This
 *     can contain formatting such as +, ( and -, as well as a phone number
 *     extension.
 * @param {?string} defaultRegion region that we are expecting the number to be
 *     from. This is only used if the number being parsed is not written in
 *     international format. The country calling code for the number in this
 *     case would be stored as that of the default region supplied.
 * @return {i18n.phonenumbers.PhoneNumber} a phone number proto buffer filled
 *     with the parsed number.
 * @throws {i18n.phonenumbers.Error} if the string is not considered to be a
 *     viable phone number or if no default region was supplied.
 */
- (NBPhoneNumber*)parseAndKeepRawInput:(NSString *)numberToParse defaultRegion:(NSString *)defaultRegion error:(NSError**)error
{
    if ([self isValidRegionCode:defaultRegion] == NO) {
        if (numberToParse.length > 0 && [numberToParse hasPrefix:@"+"] == NO) {
            NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid country code:%@", numberToParse]
                                                                 forKey:NSLocalizedDescriptionKey];
            if (error != NULL) {
                (*error) = [NSError errorWithDomain:@"INVALID_COUNTRY_CODE" code:0 userInfo:userInfo];
            }
        }
    }
    return [self parseHelper:numberToParse defaultRegion:defaultRegion keepRawInput:YES checkRegion:YES error:error];
}


/**
 * Parses a string and returns it in proto buffer format. This method is the
 * same as the public {@link #parse} method, with the exception that it allows
 * the default region to be nil, for use by {@link #isNumberMatch}.
 *
 * @param {?string} numberToParse number that we are attempting to parse. This
 *     can contain formatting such as +, ( and -, as well as a phone number
 *     extension.
 * @param {?string} defaultRegion region that we are expecting the number to be
 *     from. This is only used if the number being parsed is not written in
 *     international format. The country calling code for the number in this
 *     case would be stored as that of the default region supplied.
 * @param {boolean} keepRawInput whether to populate the raw_input field of the
 *     phoneNumber with numberToParse.
 * @param {boolean} checkRegion should be set to NO if it is permitted for
 *     the default coregion to be nil or unknown ('ZZ').
 * @return {i18n.phonenumbers.PhoneNumber} a phone number proto buffer filled
 *     with the parsed number.
 * @throws {i18n.phonenumbers.Error}
 * @private
 */
- (NBPhoneNumber*)parseHelper:(NSString *)numberToParse defaultRegion:(NSString *)defaultRegion
                 keepRawInput:(BOOL)keepRawInput checkRegion:(BOOL)checkRegion error:(NSError**)error
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    numberToParse = [helper normalizeNonBreakingSpace:numberToParse];
    
    if (numberToParse == nil) {
        if (error != NULL) {
            (*error) = [self errorWithObject:[NSString stringWithFormat:@"NOT_A_NUMBER:%@", numberToParse] withDomain:@"NOT_A_NUMBER"];
        }
        
        return nil;
    } else if (numberToParse.length > MAX_INPUT_STRING_LENGTH_) {
        if (error != NULL) {
            (*error) = [self errorWithObject:[NSString stringWithFormat:@"TOO_LONG:%@", numberToParse] withDomain:@"TOO_LONG"];
        }
        
        return nil;
    }
    
    NSString *nationalNumber = @"";
    [self buildNationalNumberForParsing:numberToParse nationalNumber:&nationalNumber];
    
    if ([self isViablePhoneNumber:nationalNumber] == NO) {
        if (error != NULL) {
            (*error) = [self errorWithObject:[NSString stringWithFormat:@"NOT_A_NUMBER:%@", nationalNumber] withDomain:@"NOT_A_NUMBER"];
        }
        
        return nil;
    }
    
    // Check the region supplied is valid, or that the extracted number starts
    // with some sort of + sign so the number's region can be determined.
    if (checkRegion && [self checkRegionForParsing:nationalNumber defaultRegion:defaultRegion] == NO) {
        if (error != NULL) {
            (*error) = [self errorWithObject:[NSString stringWithFormat:@"INVALID_COUNTRY_CODE:%@", defaultRegion]
                                  withDomain:@"INVALID_COUNTRY_CODE"];
        }
        
        return nil;
    }
    
    NBPhoneNumber *phoneNumber = [[NBPhoneNumber alloc] init];
    if (keepRawInput) {
        phoneNumber.rawInput = [numberToParse copy];
    }
    
    // Attempt to parse extension first, since it doesn't require region-specific
    // data and we want to have the non-normalised number here.
    NSString *extension = [self maybeStripExtension:&nationalNumber];
    if (extension.length > 0) {
        phoneNumber.extension = [extension copy];
    }
    
    NBPhoneMetaData *regionMetadata = [helper getMetadataForRegion:defaultRegion];
    // Check to see if the number is given in international format so we know
    // whether this number is from the default region or not.
    NSString *normalizedNationalNumber = @"";
    NSNumber *countryCode = nil;
    NSString *nationalNumberStr = [nationalNumber copy];
    
    {
        NSError *anError = nil;
        countryCode = [self maybeExtractCountryCode:nationalNumberStr
                                           metadata:regionMetadata
                                     nationalNumber:&normalizedNationalNumber
                                       keepRawInput:keepRawInput
                                        phoneNumber:&phoneNumber error:&anError];
        
        if (anError != nil) {
            if ([anError.domain isEqualToString:@"INVALID_COUNTRY_CODE"] && [self stringPositionByRegex:nationalNumberStr
                                                                                                 regex:LEADING_PLUS_CHARS_PATTERN] >= 0)
            {
                // Strip the plus-char, and try again.
                NSError *aNestedError = nil;
                nationalNumberStr = [self replaceStringByRegex:nationalNumberStr regex:LEADING_PLUS_CHARS_PATTERN withTemplate:@""];
                countryCode = [self maybeExtractCountryCode:nationalNumberStr
                                                   metadata:regionMetadata
                                             nationalNumber:&normalizedNationalNumber
                                               keepRawInput:keepRawInput
                                                phoneNumber:&phoneNumber error:&aNestedError];
                if ([countryCode isEqualToNumber:@0]) {
                    if (error != NULL)
                        (*error) = [self errorWithObject:anError.description withDomain:anError.domain];
                    
                    return nil;
                }
            } else {
                if (error != NULL)
                    (*error) = [self errorWithObject:anError.description withDomain:anError.domain];
                
                return nil;
            }
        }
    }
    
    if (![countryCode isEqualToNumber:@0]) {
        NSString *phoneNumberRegion = [self getRegionCodeForCountryCode:countryCode];
        if (phoneNumberRegion != defaultRegion) {
            // Metadata cannot be nil because the country calling code is valid.
            regionMetadata = [self getMetadataForRegionOrCallingCode:countryCode regionCode:phoneNumberRegion];
        }
    } else {
        // If no extracted country calling code, use the region supplied instead.
        // The national number is just the normalized version of the number we were
        // given to parse.
        [self normalizeSB:&nationalNumber];
        normalizedNationalNumber = [normalizedNationalNumber stringByAppendingString:nationalNumber];
        
        if (defaultRegion != nil) {
            countryCode = regionMetadata.countryCode;
            phoneNumber.countryCode = countryCode;
        } else if (keepRawInput) {
            [phoneNumber clearCountryCodeSource];
        }
    }
    
    if (normalizedNationalNumber.length < MIN_LENGTH_FOR_NSN_){
        if (error != NULL) {
            (*error) = [self errorWithObject:[NSString stringWithFormat:@"TOO_SHORT_NSN:%@", normalizedNationalNumber] withDomain:@"TOO_SHORT_NSN"];
        }
        
        return nil;
    }
    
    if (regionMetadata != nil) {
        NSString *carrierCode = @"";
        [self maybeStripNationalPrefixAndCarrierCode:&normalizedNationalNumber metadata:regionMetadata carrierCode:&carrierCode];
        
        if (keepRawInput) {
            phoneNumber.preferredDomesticCarrierCode = [carrierCode copy];
        }
    }
    
    NSString *normalizedNationalNumberStr = [normalizedNationalNumber copy];
    
    unsigned int lengthOfNationalNumber = (unsigned int)normalizedNationalNumberStr.length;
    if (lengthOfNationalNumber < MIN_LENGTH_FOR_NSN_) {
        if (error != NULL) {
            (*error) = [self errorWithObject:[NSString stringWithFormat:@"TOO_SHORT_NSN:%@", normalizedNationalNumber] withDomain:@"TOO_SHORT_NSN"];
        }
        
        return nil;
    }
    
    if (lengthOfNationalNumber > MAX_LENGTH_FOR_NSN_) {
        if (error != NULL) {
            (*error) = [self errorWithObject:[NSString stringWithFormat:@"TOO_LONG:%@", normalizedNationalNumber] withDomain:@"TOO_LONG"];
        }
        
        return nil;
    }
    
    if ([normalizedNationalNumberStr hasPrefix:@"0"]) {
        phoneNumber.italianLeadingZero = YES;
    }
    
    phoneNumber.nationalNumber =  [NSNumber numberWithLongLong:[normalizedNationalNumberStr longLongValue]];
    return phoneNumber;
}


/**
 * Converts numberToParse to a form that we can parse and write it to
 * nationalNumber if it is written in RFC3966; otherwise extract a possible
 * number out of it and write to nationalNumber.
 *
 * @param {?string} numberToParse number that we are attempting to parse. This
 *     can contain formatting such as +, ( and -, as well as a phone number
 *     extension.
 * @param {!goog.string.StringBuffer} nationalNumber a string buffer for storing
 *     the national significant number.
 * @private
 */
- (void)buildNationalNumberForParsing:(NSString *)numberToParse nationalNumber:(NSString **)nationalNumber
{
    if (nationalNumber == NULL)
        return;
    
    int indexOfPhoneContext = [self indexOfStringByString:numberToParse target:RFC3966_PHONE_CONTEXT];
    if (indexOfPhoneContext > 0)
    {
        unsigned int phoneContextStart = indexOfPhoneContext + (unsigned int)RFC3966_PHONE_CONTEXT.length;
        // If the phone context contains a phone number prefix, we need to capture
        // it, whereas domains will be ignored.
        if ([numberToParse characterAtIndex:phoneContextStart] == '+')
        {
            // Additional parameters might follow the phone context. If so, we will
            // remove them here because the parameters after phone context are not
            // important for parsing the phone number.
            NSRange foundRange = [numberToParse rangeOfString:@";" options:NSLiteralSearch range:NSMakeRange(phoneContextStart, numberToParse.length - phoneContextStart)];
            if (foundRange.location != NSNotFound)
            {
                NSRange subRange = NSMakeRange(phoneContextStart, foundRange.location - phoneContextStart);
                (*nationalNumber) = [(*nationalNumber) stringByAppendingString:[numberToParse substringWithRange:subRange]];
            }
            else
            {
                (*nationalNumber) = [(*nationalNumber) stringByAppendingString:[numberToParse substringFromIndex:phoneContextStart]];
            }
        }
        
        // Now append everything between the "tel:" prefix and the phone-context.
        // This should include the national number, an optional extension or
        // isdn-subaddress component.
        unsigned int rfc3966Start = [self indexOfStringByString:numberToParse target:RFC3966_PREFIX] + (unsigned int)RFC3966_PREFIX.length;
        NSString *subString = [numberToParse substringWithRange:NSMakeRange(rfc3966Start, indexOfPhoneContext - rfc3966Start)];
        (*nationalNumber) = [(*nationalNumber) stringByAppendingString:subString];
    }
    else
    {
        // Extract a possible number from the string passed in (this strips leading
        // characters that could not be the start of a phone number.)
        (*nationalNumber) = [(*nationalNumber) stringByAppendingString:[self extractPossibleNumber:numberToParse]];
    }
    
    // Delete the isdn-subaddress and everything after it if it is present.
    // Note extension won't appear at the same time with isdn-subaddress
    // according to paragraph 5.3 of the RFC3966 spec,
    NSString *nationalNumberStr = [(*nationalNumber) copy];
    int indexOfIsdn = [self indexOfStringByString:nationalNumberStr target:RFC3966_ISDN_SUBADDRESS];
    if (indexOfIsdn > 0)
    {
        (*nationalNumber) = @"";
        (*nationalNumber) = [(*nationalNumber) stringByAppendingString:[nationalNumberStr substringWithRange:NSMakeRange(0, indexOfIsdn)]];
    }
    // If both phone context and isdn-subaddress are absent but other
    // parameters are present, the parameters are left in nationalNumber. This
    // is because we are concerned about deleting content from a potential
    // number string when there is no strong evidence that the number is
    // actually written in RFC3966.
}


/**
 * Takes two phone numbers and compares them for equality.
 *
 * <p>Returns EXACT_MATCH if the country_code, NSN, presence of a leading zero
 * for Italian numbers and any extension present are the same. Returns NSN_MATCH
 * if either or both has no region specified, and the NSNs and extensions are
 * the same. Returns SHORT_NSN_MATCH if either or both has no region specified,
 * or the region specified is the same, and one NSN could be a shorter version
 * of the other number. This includes the case where one has an extension
 * specified, and the other does not. Returns NO_MATCH otherwise. For example,
 * the numbers +1 345 657 1234 and 657 1234 are a SHORT_NSN_MATCH. The numbers
 * +1 345 657 1234 and 345 657 are a NO_MATCH.
 *
 * @param {i18n.phonenumbers.PhoneNumber|string} firstNumberIn first number to
 *     compare. If it is a string it can contain formatting, and can have
 *     country calling code specified with + at the start.
 * @param {i18n.phonenumbers.PhoneNumber|string} secondNumberIn second number to
 *     compare. If it is a string it can contain formatting, and can have
 *     country calling code specified with + at the start.
 * @return {MatchType} NOT_A_NUMBER, NO_MATCH,
 *     SHORT_NSN_MATCH, NSN_MATCH or EXACT_MATCH depending on the level of
 *     equality of the two numbers, described in the method definition.
 */
- (NBEMatchType)isNumberMatch:(id)firstNumberIn second:(id)secondNumberIn error:(NSError**)error
{
    NBEMatchType res = 0;
    @try {
        res = [self isNumberMatch:firstNumberIn second:secondNumberIn];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    return res;
}

- (NBEMatchType)isNumberMatch:(id)firstNumberIn second:(id)secondNumberIn
{
    // If the input arguements are strings parse them to a proto buffer format.
    // Else make copies of the phone numbers so that the numbers passed in are not
    // edited.
    /** @type {i18n.phonenumbers.PhoneNumber} */
    NBPhoneNumber *firstNumber = nil, *secondNumber = nil;
    
    if ([firstNumberIn isKindOfClass:[NSString class]]) {
        // First see if the first number has an implicit country calling code, by
        // attempting to parse it.
        NSError *anError;
        firstNumber = [self parse:firstNumberIn defaultRegion:UNKNOWN_REGION_ error:&anError];
        
        if (anError != nil) {
            if ([anError.domain isEqualToString:@"INVALID_COUNTRY_CODE"] == NO) {
                return NBEMatchTypeNOT_A_NUMBER;
            }
            // The first number has no country calling code. EXACT_MATCH is no longer
            // possible. We parse it as if the region was the same as that for the
            // second number, and if EXACT_MATCH is returned, we replace this with
            // NSN_MATCH.
            if ([secondNumberIn isKindOfClass:[NSString class]] == NO) {
                NSString *secondNumberRegion = [self getRegionCodeForCountryCode:((NBPhoneNumber*)secondNumberIn).countryCode];
                if (secondNumberRegion != UNKNOWN_REGION_) {
                    NSError *aNestedError;
                    firstNumber = [self parse:firstNumberIn defaultRegion:secondNumberRegion error:&aNestedError];
                    
                    if (aNestedError != nil) {
                        return NBEMatchTypeNOT_A_NUMBER;
                    }
                    
                    NBEMatchType match = [self isNumberMatch:firstNumber second:secondNumberIn];
                    if (match == NBEMatchTypeEXACT_MATCH) {
                        return NBEMatchTypeNSN_MATCH;
                    }
                    return match;
                }
            }
            // If the second number is a string or doesn't have a valid country
            // calling code, we parse the first number without country calling code.
            NSError *aNestedError;
            firstNumber = [self parseHelper:firstNumberIn defaultRegion:nil keepRawInput:NO checkRegion:NO error:&aNestedError];
            if (aNestedError != nil) {
                return NBEMatchTypeNOT_A_NUMBER;
            }
        }
    } else {
        firstNumber = [firstNumberIn copy];
    }
    
    if ([secondNumberIn isKindOfClass:[NSString class]]) {
        NSError *parseError;
        secondNumber = [self parse:secondNumberIn defaultRegion:UNKNOWN_REGION_ error:&parseError];
        if (parseError != nil) {
            if ([parseError.domain isEqualToString:@"INVALID_COUNTRY_CODE"] == NO) {
                return NBEMatchTypeNOT_A_NUMBER;
            }
            return [self isNumberMatch:secondNumberIn second:firstNumber];
        } else {
            return [self isNumberMatch:firstNumberIn second:secondNumber];
        }
    }
    else {
        secondNumber = [secondNumberIn copy];
    }
    
    // First clear raw_input, country_code_source and
    // preferred_domestic_carrier_code fields and any empty-string extensions so
    // that we can use the proto-buffer equality method.
    firstNumber.rawInput = @"";
    [firstNumber clearCountryCodeSource];
    firstNumber.preferredDomesticCarrierCode = @"";
    
    secondNumber.rawInput = @"";
    [secondNumber clearCountryCodeSource];
    secondNumber.preferredDomesticCarrierCode = @"";
    
    if (firstNumber.extension != nil && firstNumber.extension.length == 0) {
        firstNumber.extension = nil;
    }
    
    if (secondNumber.extension != nil && secondNumber.extension.length == 0) {
        secondNumber.extension = nil;
    }
    
    // Early exit if both had extensions and these are different.
    if ([NBMetadataHelper hasValue:firstNumber.extension] && [NBMetadataHelper hasValue:secondNumber.extension] &&
        [firstNumber.extension isEqualToString:secondNumber.extension] == NO) {
        return NBEMatchTypeNO_MATCH;
    }
    
    NSNumber *firstNumberCountryCode = firstNumber.countryCode;
    NSNumber *secondNumberCountryCode = secondNumber.countryCode;
    
    // Both had country_code specified.
    if (![firstNumberCountryCode isEqualToNumber:@0] && ![secondNumberCountryCode isEqualToNumber:@0]) {
        if ([firstNumber isEqual:secondNumber]) {
            return NBEMatchTypeEXACT_MATCH;
        } else if ([firstNumberCountryCode isEqualToNumber:secondNumberCountryCode] && [self isNationalNumberSuffixOfTheOther:firstNumber second:secondNumber]) {
            // A SHORT_NSN_MATCH occurs if there is a difference because of the
            // presence or absence of an 'Italian leading zero', the presence or
            // absence of an extension, or one NSN being a shorter variant of the
            // other.
            return NBEMatchTypeSHORT_NSN_MATCH;
        }
        // This is not a match.
        return NBEMatchTypeNO_MATCH;
    }
    // Checks cases where one or both country_code fields were not specified. To
    // make equality checks easier, we first set the country_code fields to be
    // equal.
    firstNumber.countryCode = @0;
    secondNumber.countryCode = @0;
    // If all else was the same, then this is an NSN_MATCH.
    if ([firstNumber isEqual:secondNumber]) {
        return NBEMatchTypeNSN_MATCH;
    }
    
    if ([self isNationalNumberSuffixOfTheOther:firstNumber second:secondNumber]) {
        return NBEMatchTypeSHORT_NSN_MATCH;
    }
    return NBEMatchTypeNO_MATCH;
}


/**
 * Returns NO when one national number is the suffix of the other or both are
 * the same.
 *
 * @param {i18n.phonenumbers.PhoneNumber} firstNumber the first PhoneNumber
 *     object.
 * @param {i18n.phonenumbers.PhoneNumber} secondNumber the second PhoneNumber
 *     object.
 * @return {boolean} NO if one PhoneNumber is the suffix of the other one.
 * @private
 */
- (BOOL)isNationalNumberSuffixOfTheOther:(NBPhoneNumber*)firstNumber second:(NBPhoneNumber*)secondNumber
{
    NSString *firstNumberNationalNumber = [NSString stringWithFormat:@"%@", firstNumber.nationalNumber];
    NSString *secondNumberNationalNumber = [NSString stringWithFormat:@"%@", secondNumber.nationalNumber];
    
    // Note that endsWith returns NO if the numbers are equal.
    return [firstNumberNationalNumber hasSuffix:secondNumberNationalNumber] ||
    [secondNumberNationalNumber hasSuffix:firstNumberNationalNumber];
}


/**
 * Returns NO if the number can be dialled from outside the region, or
 * unknown. If the number can only be dialled from within the region, returns
 * NO. Does not check the number is a valid number.
 * TODO: Make this method public when we have enough metadata to make it
 * worthwhile. Currently visible for testing purposes only.
 *
 * @param {i18n.phonenumbers.PhoneNumber} number the phone-number for which we
 *     want to know whether it is diallable from outside the region.
 * @return {boolean} NO if the number can only be dialled from within the
 *     country.
 */
- (BOOL)canBeInternationallyDialled:(NBPhoneNumber*)number error:(NSError**)error
{
    BOOL res = NO;
    @try {
        res = [self canBeInternationallyDialled:number];
    }
    @catch (NSException *exception) {
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason
                                                             forKey:NSLocalizedDescriptionKey];
        if (error != NULL)
            (*error) = [NSError errorWithDomain:exception.name code:0 userInfo:userInfo];
    }
    return res;
}

- (BOOL)canBeInternationallyDialled:(NBPhoneNumber*)number
{
    NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
    NBPhoneMetaData *metadata = [helper getMetadataForRegion:[self getRegionCodeForNumber:number]];
    if (metadata == nil) {
        // Note numbers belonging to non-geographical entities (e.g. +800 numbers)
        // are always internationally diallable, and will be caught here.
        return YES;
    }
    NSString *nationalSignificantNumber = [self getNationalSignificantNumber:number];
    return [self isNumberMatchingDesc:nationalSignificantNumber numberDesc:metadata.noInternationalDialling] == NO;
}


/**
 * Check whether the entire input sequence can be matched against the regular
 * expression.
 *
 * @param {!RegExp|string} regex the regular expression to match against.
 * @param {string} str the string to test.
 * @return {boolean} NO if str can be matched entirely against regex.
 * @private
 */
- (BOOL)matchesEntirely:(NSString *)regex string:(NSString *)str
{
    if ([regex isEqualToString:@"NA"]) {
        return NO;
    }
    
    NSError *error = nil;
    NSRegularExpression *currentPattern = [self entireRegularExpressionWithPattern:regex options:0 error:&error];
    NSRange stringRange = NSMakeRange(0, str.length);
    NSTextCheckingResult *matchResult = [currentPattern firstMatchInString:str options:NSMatchingAnchored range:stringRange];
    
    if (matchResult != nil) {
        BOOL matchIsEntireString = NSEqualRanges(matchResult.range, stringRange);
        if (matchIsEntireString)
        {
            return YES;
        }
    }
    
    return NO;
}

@end
