text:
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html

#include "unicode/utypes.h"

#if !UCONFIG_NO_FORMATTING

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include "unicode/localpointer.h"
#include "unicode/plurrule.h"
#include "unicode/stringpiece.h"
#include "unicode/numberformatter.h"
#include "unicode/numberrangeformatter.h"

#include "cmemory.h"
#include "cstr.h"
#include "plurrule_impl.h"
#include "plurults.h"
#include "uhash.h"
#include "number_decimalquantity.h"

using icu::number::impl::DecimalQuantity;
using namespace icu::number;

void setupResult(const int32_t testSource[], char result[], int32_t* max);
UBool checkEqual(const PluralRules &test, char *result, int32_t max);
UBool testEquality(const PluralRules &test);

// This is an API test, not a unit test.  It doesn't test very many cases, and doesn't
// try to test the full functionality.  It just calls each function in the class and
// verifies that it works on a basic level.

void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
{
    if (exec) logln("TestSuite PluralRulesAPI");
    TESTCASE_AUTO_BEGIN;
    TESTCASE_AUTO(testAPI);
    // TESTCASE_AUTO(testGetUniqueKeywordValue);
    TESTCASE_AUTO(testGetSamples);
    TESTCASE_AUTO(testGetFixedDecimalSamples);
    TESTCASE_AUTO(testSamplesWithExponent);
    TESTCASE_AUTO(testWithin);
    TESTCASE_AUTO(testGetAllKeywordValues);
    TESTCASE_AUTO(testCompactDecimalPluralKeyword);
    TESTCASE_AUTO(testOrdinal);
    TESTCASE_AUTO(testSelect);
    TESTCASE_AUTO(testSelectRange);
    TESTCASE_AUTO(testAvailbleLocales);
    TESTCASE_AUTO(testParseErrors);
    TESTCASE_AUTO(testFixedDecimal);
    TESTCASE_AUTO(testSelectTrailingZeros);
    TESTCASE_AUTO(testLocaleExtension);
    TESTCASE_AUTO_END;
}

// Quick and dirty class for putting UnicodeStrings in char * messages.
//   TODO: something like this should be generally available.
class US {
  private:
    char *buf;
  public:
    US(const UnicodeString &us) {
       int32_t bufLen = us.extract((int32_t)0, us.length(), (char *)NULL, (uint32_t)0) + 1;
       buf = (char *)uprv_malloc(bufLen);
       us.extract(0, us.length(), buf, bufLen); }
    const char *cstr() {return buf;}
    ~US() { uprv_free(buf);}
};

#define PLURAL_TEST_NUM    18
/**
 * Test various generic API methods of PluralRules for API coverage.
 */
void PluralRulesTest::testAPI(/*char *par*/)
{
    UnicodeString pluralTestData[PLURAL_TEST_NUM] = {
            UNICODE_STRING_SIMPLE("a: n is 1"),
            UNICODE_STRING_SIMPLE("a: n mod 10 is 2"),
            UNICODE_STRING_SIMPLE("a: n is not 1"),
            UNICODE_STRING_SIMPLE("a: n mod 3 is not 1"),
            UNICODE_STRING_SIMPLE("a: n in 2..5"),
            UNICODE_STRING_SIMPLE("a: n within 2..5"),
            UNICODE_STRING_SIMPLE("a: n not in 2..5"),
            UNICODE_STRING_SIMPLE("a: n not within 2..5"),
            UNICODE_STRING_SIMPLE("a: n mod 10 in 2..5"),
            UNICODE_STRING_SIMPLE("a: n mod 10 within 2..5"),
            UNICODE_STRING_SIMPLE("a: n mod 10 is 2 and n is not 12"),
            UNICODE_STRING_SIMPLE("a: n mod 10 in 2..3 or n mod 10 is 5"),
            UNICODE_STRING_SIMPLE("a: n mod 10 within 2..3 or n mod 10 is 5"),
            UNICODE_STRING_SIMPLE("a: n is 1 or n is 4 or n is 23"),
            UNICODE_STRING_SIMPLE("a: n mod 2 is 1 and n is not 3 and n in 1..11"),
            UNICODE_STRING_SIMPLE("a: n mod 2 is 1 and n is not 3 and n within 1..11"),
            UNICODE_STRING_SIMPLE("a: n mod 2 is 1 or n mod 5 is 1 and n is not 6"),
            "",
    };
    static const int32_t pluralTestResult[PLURAL_TEST_NUM][30] = {
        {1, 0},
        {2,12,22, 0},
        {0,2,3,4,5,0},
        {0,2,3,5,6,8,9,0},
        {2,3,4,5,0},
        {2,3,4,5,0},
        {0,1,6,7,8, 0},
        {0,1,6,7,8, 0},
        {2,3,4,5,12,13,14,15,22,23,24,25,0},
        {2,3,4,5,12,13,14,15,22,23,24,25,0},
        {2,22,32,42,0},
        {2,3,5,12,13,15,22,23,25,0},
        {2,3,5,12,13,15,22,23,25,0},
        {1,4,23,0},
        {1,5,7,9,11,0},
        {1,5,7,9,11,0},
        {1,3,5,7,9,11,13,15,16,0},
    };
    UErrorCode status = U_ZERO_ERROR;

    // ======= Test constructors
    logln("Testing PluralRules constructors");

    logln("\n start default locale test case ..\n");

    PluralRules defRule(status);
    LocalPointer<PluralRules> test(new PluralRules(status), status);
    if(U_FAILURE(status)) {
        dataerrln("ERROR: Could not create PluralRules (default) - exitting");
        return;
    }
    LocalPointer<PluralRules> newEnPlural(test->forLocale(Locale::getEnglish(), status), status);
    if(U_FAILURE(status)) {
        dataerrln("ERROR: Could not create PluralRules (English) - exitting");
        return;
    }

    // ======= Test clone, assignment operator && == operator.
    LocalPointer<PluralRules> dupRule(defRule.clone());
    if (dupRule==NULL) {
        errln("ERROR: clone plural rules test failed!");
        return;
    } else {
        if ( *dupRule != defRule ) {
            errln("ERROR:  clone plural rules test failed!");
        }
    }
    *dupRule = *newEnPlural;
    if (dupRule!=NULL) {
        if ( *dupRule != *newEnPlural ) {
            errln("ERROR:  clone plural rules test failed!");
        }
    }

    // ======= Test empty plural rules
    logln("Testing Simple PluralRules");

    LocalPointer<PluralRules> empRule(test->createRules(UNICODE_STRING_SIMPLE("a:n"), status));
    UnicodeString key;
    for (int32_t i=0; i<10; ++i) {
        key = empRule->select(i);
        if ( key.charAt(0)!= 0x61 ) { // 'a'
            errln("ERROR:  empty plural rules test failed! - exitting");
        }
    }

    // ======= Test simple plural rules
    logln("Testing Simple PluralRules");

    char result[100];
    int32_t max;

    for (int32_t i=0; i<PLURAL_TEST_NUM-1; ++i) {
       LocalPointer<PluralRules> newRules(test->createRules(pluralTestData[i], status));
       setupResult(pluralTestResult[i], result, &max);
       if ( !checkEqual(*newRules, result, max) ) {
            errln("ERROR:  simple plural rules failed! - exitting");
            return;
        }
    }

    // ======= Test complex plural rules
    logln("Testing Complex PluralRules");
    // TODO: the complex test data is hard coded. It's better to implement
    // a parser to parse the test data.
    UnicodeString complexRule = UNICODE_STRING_SIMPLE("a: n in 2..5; b: n in 5..8; c: n mod 2 is 1");
    UnicodeString complexRule2 = UNICODE_STRING_SIMPLE("a: n within 2..5; b: n within 5..8; c: n mod 2 is 1");
    char cRuleResult[] =
    {
       0x6F, // 'o'
       0x63, // 'c'
       0x61, // 'a'
       0x61, // 'a'
       0x61, // 'a'
       0x61, // 'a'
       0x62, // 'b'
       0x62, // 'b'
       0x62, // 'b'
       0x63, // 'c'
       0x6F, // 'o'
       0x63  // 'c'
    };
    LocalPointer<PluralRules> newRules(test->createRules(complexRule, status));
    if ( !checkEqual(*newRules, cRuleResult, 12) ) {
         errln("ERROR:  complex plural rules failed! - exitting");
         return;
    }
    newRules.adoptInstead(test->createRules(complexRule2, status));
    if ( !checkEqual(*newRules, cRuleResult, 12) ) {
         errln("ERROR:  complex plural rules failed! - exitting");
         return;
    }

    // ======= Test decimal fractions plural rules
    UnicodeString decimalRule= UNICODE_STRING_SIMPLE("a: n not in 0..100;");
    UnicodeString KEYWORD_A = UNICODE_STRING_SIMPLE("a");
    status = U_ZERO_ERROR;
    newRules.adoptInstead(test->createRules(decimalRule, status));
    if (U_FAILURE(status)) {
        dataerrln("ERROR: Could not create PluralRules for testing fractions - exitting");
        return;
    }
    double fData[] =     {-101, -100, -1,     -0.0,  0,     0.1,  1,     1.999,  2.0,   100,   100.001 };
    UBool isKeywordA[] = {TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE,   FALSE, FALSE, TRUE };
    for (int32_t i=0; i<UPRV_LENGTHOF(fData); i++) {
        if ((newRules->select(fData[i])== KEYWORD_A) != isKeywordA[i]) {
             errln("File %s, Line %d, ERROR: plural rules for decimal fractions test failed!\n"
                   "  number = %g, expected %s", __FILE__, __LINE__, fData[i], isKeywordA[i]?"TRUE":"FALSE");
        }
    }

    // ======= Test Equality
    logln("Testing Equality of PluralRules");

    if ( !testEquality(*test) ) {
         errln("ERROR:  complex plural rules failed! - exitting");
         return;
     }

    // ======= Test getStaticClassID()
    logln("Testing getStaticClassID()");

    if(test->getDynamicClassID() != PluralRules::getStaticClassID()) {
        errln("ERROR: getDynamicClassID() didn't return the expected value");
    }
    // ====== Test fallback to parent locale
    LocalPointer<PluralRules> en_UK(test->forLocale(Locale::getUK(), status));
    LocalPointer<PluralRules> en(test->forLocale(Locale::getEnglish(), status));
    if (en_UK.isValid() && en.isValid()) {
        if ( *en_UK != *en ) {
            errln("ERROR:  test locale fallback failed!");
        }
    }

    LocalPointer<PluralRules> zh_Hant(test->forLocale(Locale::getTaiwan(), status));
    LocalPointer<PluralRules> zh(test->forLocale(Locale::getChinese(), status));
    if (zh_Hant.isValid() && zh.isValid()) {
        if ( *zh_Hant != *zh ) {
            errln("ERROR:  test locale fallback failed!");
        }
    }
}

void setupResult(const int32_t testSource[], char result[], int32_t* max) {
    int32_t i=0;
    int32_t curIndex=0;

    do {
        while (curIndex < testSource[i]) {
            result[curIndex++]=0x6F; //'o' other
        }
        result[curIndex++]=0x61; // 'a'

    } while(testSource[++i]>0);
    *max=curIndex;
}

UBool checkEqual(const PluralRules &test, char *result, int32_t max) {
    UnicodeString key;
    UBool isEqual = TRUE;
    for (int32_t i=0; i<max; ++i) {
        key= test.select(i);
        if ( key.charAt(0)!=result[i] ) {
            isEqual = FALSE;
        }
    }
    return isEqual;
}

static const int32_t MAX_EQ_ROW = 2;
static const int32_t MAX_EQ_COL = 5;
UBool testEquality(const PluralRules &test) {
    UnicodeString testEquRules[MAX_EQ_ROW][MAX_EQ_COL] = {
        {   UNICODE_STRING_SIMPLE("a: n in 2..3"),
            UNICODE_STRING_SIMPLE("a: n is 2 or n is 3"),
            UNICODE_STRING_SIMPLE( "a:n is 3 and n in 2..5 or n is 2"),
            "",
        },
        {   UNICODE_STRING_SIMPLE("a: n is 12; b:n mod 10 in 2..3"),
            UNICODE_STRING_SIMPLE("b: n mod 10 in 2..3 and n is not 12; a: n in 12..12"),
            UNICODE_STRING_SIMPLE("b: n is 13; a: n in 12..13; b: n mod 10 is 2 or n mod 10 is 3"),
            "",
        }
    };
    UErrorCode status = U_ZERO_ERROR;
    UnicodeString key[MAX_EQ_COL];
    UBool ret=TRUE;
    for (int32_t i=0; i<MAX_EQ_ROW; ++i) {
        PluralRules* rules[MAX_EQ_COL];

        for (int32_t j=0; j<MAX_EQ_COL; ++j) {
            rules[j]=NULL;
        }
        int32_t totalRules=0;
        while((totalRules<MAX_EQ_COL) && (testEquRules[i][totalRules].length()>0) ) {
            rules[totalRules]=test.createRules(testEquRules[i][totalRules], status);
            totalRules++;
        }
        for (int32_t n=0; n<300 && ret ; ++n) {
            for(int32_t j=0; j<totalRules;++j) {
                key[j] = rules[j]->select(n);
            }
            for(int32_t j=0; j<totalRules-1;++j) {
                if (key[j]!=key[j+1]) {
                    ret= FALSE;
                    break;
                }
            }

        }
        for (int32_t j=0; j<MAX_EQ_COL; ++j) {
            if (rules[j]!=NULL) {
                delete rules[j];
            }
        }
    }

    return ret;
}

void
PluralRulesTest::assertRuleValue(const UnicodeString& rule, double expected) {
    assertRuleKeyValue("a:" + rule, "a", expected);
}

void
PluralRulesTest::assertRuleKeyValue(const UnicodeString& rule,
                                    const UnicodeString& key, double expected) {
    UErrorCode status = U_ZERO_ERROR;
    PluralRules *pr = PluralRules::createRules(rule, status);
    double result = pr->getUniqueKeywordValue(key);
    delete pr;
    if (expected != result) {
        errln("expected %g but got %g", expected, result);
    }
}

// TODO: UniqueKeywordValue() is not currently supported.
//       If it never will be, this test code should be removed.
void PluralRulesTest::testGetUniqueKeywordValue() {
    assertRuleValue("n is 1", 1);
    assertRuleValue("n in 2..2", 2);
    assertRuleValue("n within 2..2", 2);
    assertRuleValue("n in 3..4", UPLRULES_NO_UNIQUE_VALUE);
    assertRuleValue("n within 3..4", UPLRULES_NO_UNIQUE_VALUE);
    assertRuleValue("n is 2 or n is 2", 2);
    assertRuleValue("n is 2 and n is 2", 2);
    assertRuleValue("n is 2 or n is 3", UPLRULES_NO_UNIQUE_VALUE);
    assertRuleValue("n is 2 and n is 3", UPLRULES_NO_UNIQUE_VALUE);
    assertRuleValue("n is 2 or n in 2..3", UPLRULES_NO_UNIQUE_VALUE);
    assertRuleValue("n is 2 and n in 2..3", 2);
    assertRuleKeyValue("a: n is 1", "not_defined", UPLRULES_NO_UNIQUE_VALUE); // key not defined
    assertRuleKeyValue("a: n is 1", "other", UPLRULES_NO_UNIQUE_VALUE); // key matches default rule
}

void PluralRulesTest::testGetSamples() {
    // TODO: fix samples, re-enable this test.

    // no get functional equivalent API in ICU4C, so just
    // test every locale...
    UErrorCode status = U_ZERO_ERROR;
    int32_t numLocales;
    const Locale* locales = Locale::getAvailableLocales(numLocales);

    double values[1000];
    for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) {
        if (uprv_strcmp(locales[i].getLanguage(), "fr") == 0 &&
                logKnownIssue("21299", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
            continue;
        }
        LocalPointer<PluralRules> rules(PluralRules::forLocale(locales[i], status));
        if (U_FAILURE(status)) {
            break;
        }
        LocalPointer<StringEnumeration> keywords(rules->getKeywords(status));
        if (U_FAILURE(status)) {
            break;
        }
        const UnicodeString* keyword;
        while (NULL != (keyword = keywords->snext(status))) {
            int32_t count = rules->getSamples(*keyword, values, UPRV_LENGTHOF(values), status);
            if (U_FAILURE(status)) {
                errln(UnicodeString(u"getSamples() failed for locale ") +
                      locales[i].getName() +
                      UnicodeString(u", keyword ") + *keyword);
                continue;
            }
            if (count == 0) {
                // TODO: Lots of these.
                //   errln(UnicodeString(u"no samples for keyword ") + *keyword + UnicodeString(u" in locale ") + locales[i].getName() );
            }
            if (count > UPRV_LENGTHOF(values)) {
                errln(UnicodeString(u"getSamples()=") + count +
                      UnicodeString(u", too many values, for locale ") +
                      locales[i].getName() +
                      UnicodeString(u", keyword ") + *keyword);
                count = UPRV_LENGTHOF(values);
            }
            for (int32_t j = 0; j < count; ++j) {
                if (values[j] == UPLRULES_NO_UNIQUE_VALUE) {
                    errln("got 'no unique value' among values");
                } else {
                    UnicodeString resultKeyword = rules->select(values[j]);
                    // if (strcmp(locales[i].getName(), "uk") == 0) {    // Debug only.
                    //     std::cout << "  uk " << US(resultKeyword).cstr() << " " << values[j] << std::endl;
                    // }
                    if (*keyword != resultKeyword) {
                        errln("file %s, line %d, Locale %s, sample for keyword \"%s\":  %g, select(%g) returns keyword \"%s\"",
                              __FILE__, __LINE__, locales[i].getName(), US(*keyword).cstr(), values[j], values[j], US(resultKeyword).cstr());
                    }
                }
            }
        }
    }
}

void PluralRulesTest::testGetFixedDecimalSamples() {
    // TODO: fix samples, re-enable this test.

    // no get functional equivalent API in ICU4C, so just
    // test every locale...
    UErrorCode status = U_ZERO_ERROR;
    int32_t numLocales;
    const Locale* locales = Locale::getAvailableLocales(numLocales);

    FixedDecimal values[1000];
    for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) {
        if (uprv_strcmp(locales[i].getLanguage(), "fr") == 0 &&
                logKnownIssue("21299", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
            continue;
        }
        LocalPointer<PluralRules> rules(PluralRules::forLocale(locales[i], status));
        if (U_FAILURE(status)) {
            break;
        }
        LocalPointer<StringEnumeration> keywords(rules->getKeywords(status));
        if (U_FAILURE(status)) {
            break;
        }
        const UnicodeString* keyword;
        while (NULL != (keyword = keywords->snext(status))) {
            int32_t count = rules->getSamples(*keyword, values, UPRV_LENGTHOF(values), status);
            if (U_FAILURE(status)) {
                errln(UnicodeString(u"getSamples() failed for locale ") +
                      locales[i].getName() +
                      UnicodeString(u", keyword ") + *keyword);
                continue;
            }
            if (count == 0) {
                // TODO: Lots of these.
                //   errln(UnicodeString(u"no samples for keyword ") + *keyword + UnicodeString(u" in locale ") + locales[i].getName() );
            }
            if (count > UPRV_LENGTHOF(values)) {
                errln(UnicodeString(u"getSamples()=") + count +
                      UnicodeString(u", too many values, for locale ") +
                      locales[i].getName() +
                      UnicodeString(u", keyword ") + *keyword);
                count = UPRV_LENGTHOF(values);
            }
            for (int32_t j = 0; j < count; ++j) {
                if (values[j] == UPLRULES_NO_UNIQUE_VALUE_DECIMAL) {
                    errln("got 'no unique value' among values");
                } else {
                    UnicodeString resultKeyword = rules->select(values[j]);
                    // if (strcmp(locales[i].getName(), "uk") == 0) {    // Debug only.
                    //     std::cout << "  uk " << US(resultKeyword).cstr() << " " << values[j] << std::endl;
                    // }
                    if (*keyword != resultKeyword) {
                        errln("file %s, line %d, Locale %s, sample for keyword \"%s\":  %s, select(%s) returns keyword \"%s\"",
                                  __FILE__, __LINE__, locales[i].getName(), US(*keyword).cstr(), values[j].toString().getBuffer(), values[j].toString().getBuffer(), US(resultKeyword).cstr());
                    }
                }
            }
        }
    }
}

void PluralRulesTest::testSamplesWithExponent() {
    // integer samples
    UErrorCode status = U_ZERO_ERROR;
    UnicodeString description(
        u"one: i = 0,1 @integer 0, 1, 1e5 @decimal 0.0~1.5, 1.1e5; "
        u"many: e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5"
        u" @integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, … @decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …; "
        u"other:  @integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …"
        u" @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …"
    );
    LocalPointer<PluralRules> test(PluralRules::createRules(description, status));
    if (U_FAILURE(status)) {
        errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
        return;
    }
    checkNewSamples(description, test, u"one", u"@integer 0, 1, 1e5", FixedDecimal(0));
    checkNewSamples(description, test, u"many", u"@integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, …", FixedDecimal(1000000));
    checkNewSamples(description, test, u"other", u"@integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …", FixedDecimal(2));

    // decimal samples
    status = U_ZERO_ERROR;
    UnicodeString description2(
        u"one: i = 0,1 @decimal 0.0~1.5, 1.1e5; "
        u"many: e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5"
        u" @decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …; "
        u"other:  @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …"
    );
    LocalPointer<PluralRules> test2(PluralRules::createRules(description2, status));
    if (U_FAILURE(status)) {
        errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
        return;
    }
    checkNewSamples(description2, test2, u"one", u"@decimal 0.0~1.5, 1.1e5", FixedDecimal(0, 1));
    checkNewSamples(description2, test2, u"many", u"@decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …", FixedDecimal::createWithExponent(2.1, 1, 6));
    checkNewSamples(description2, test2, u"other", u"@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …", FixedDecimal(2.0, 1));
}

void PluralRulesTest::checkNewSamples(
        UnicodeString description,
        const LocalPointer<PluralRules> &test,
        UnicodeString keyword,
        UnicodeString samplesString,
        FixedDecimal firstInRange) {

    UErrorCode status = U_ZERO_ERROR;
    FixedDecimal samples[1000];

    test->getSamples(keyword, samples, UPRV_LENGTHOF(samples), status);
    if (U_FAILURE(status)) {
        errln("Couldn't retrieve plural samples, with error = %s", u_errorName(status));
        return;
    }
    FixedDecimal actualFirstSample = samples[0];

    if (!(firstInRange == actualFirstSample)) {
        CStr descCstr(description);
        CStr samplesCstr(samplesString);
        char errMsg[1000];
        snprintf(errMsg, sizeof(errMsg), "First parsed sample FixedDecimal not equal to expected for samples: %s in rule string: %s\n", descCstr(), samplesCstr());
        errln(errMsg);
    }
}

void PluralRulesTest::testWithin() {
    // goes to show you what lack of testing will do.
    // of course, this has been broken for two years and no one has noticed...
    UErrorCode status = U_ZERO_ERROR;
    PluralRules *rules = PluralRules::createRules("a: n mod 10 in 5..8", status);
    if (!rules) {
        errln("couldn't instantiate rules");
        return;
    }

    UnicodeString keyword = rules->select((int32_t)26);
    if (keyword != "a") {
        errln("expected 'a' for 26 but didn't get it.");
    }

    keyword = rules->select(26.5);
    if (keyword != "other") {
        errln("expected 'other' for 26.5 but didn't get it.");
    }

    delete rules;
}

void
PluralRulesTest::testGetAllKeywordValues() {
    const char* data[] = {
        "a: n in 2..5", "a: 2,3,4,5; other: null; b:",
        "a: n not in 2..5", "a: null; other: null",
        "a: n within 2..5", "a: null; other: null",
        "a: n not within 2..5", "a: null; other: null",
        "a: n in 2..5 or n within 6..8", "a: null", // ignore 'other' here on out, always null
        "a: n in 2..5 and n within 6..8", "a:",
        "a: n in 2..5 and n within 5..8", "a: 5",
        "a: n within 2..5 and n within 6..8", "a:", // our sampling catches these
        "a: n within 2..5 and n within 5..8", "a: 5", // ''
        "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5", "a: 2,4",
        "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5 "
          "or n within 5..6 and n within 6..7", "a: null", // but not this...
        "a: n mod 3 is 0", "a: null",
        "a: n mod 3 is 0 and n within 1..2", "a:",
        "a: n mod 3 is 0 and n within 0..5", "a: 0,3",
        "a: n mod 3 is 0 and n within 0..6", "a: null", // similarly with mod, we don't catch...
        "a: n mod 3 is 0 and n in 3..12", "a: 3,6,9,12",
        NULL
    };

    for (int i = 0; data[i] != NULL; i += 2) {
        UErrorCode status = U_ZERO_ERROR;
        UnicodeString ruleDescription(data[i], -1, US_INV);
        const char* result = data[i+1];

        logln("[%d] %s", i >> 1, data[i]);

        PluralRules *p = PluralRules::createRules(ruleDescription, status);
        if (p == NULL || U_FAILURE(status)) {
            errln("file %s, line %d: could not create rules from '%s'\n"
                  "  ErrorCode: %s\n",
                  __FILE__, __LINE__, data[i], u_errorName(status));
            continue;
        }

        // TODO: fix samples implementation, re-enable test.
        (void)result;
        #if 0

        const char* rp = result;
        while (*rp) {
            while (*rp == ' ') ++rp;
            if (!rp) {
                break;
            }

            const char* ep = rp;
            while (*ep && *ep != ':') ++ep;

            status = U_ZERO_ERROR;
            UnicodeString keyword(rp, ep - rp, US_INV);
            double samples[4]; // no test above should have more samples than 4
            int32_t count = p->getAllKeywordValues(keyword, &samples[0], 4, status);
            if (U_FAILURE(status)) {
                errln("error getting samples for %s", rp);
                break;
            }

            if (count > 4) {
              errln("count > 4 for keyword %s", rp);
              count = 4;
            }

            if (*ep) {
                ++ep; // skip colon
                while (*ep && *ep == ' ') ++ep; // and spaces
            }

            UBool ok = TRUE;
            if (count == -1) {
                if (*ep != 'n') {
                    errln("expected values for keyword %s but got -1 (%s)", rp, ep);
                    ok = FALSE;
                }
            } else if (*ep == 'n') {
                errln("expected count of -1, got %d, for keyword %s (%s)", count, rp, ep);
                ok = FALSE;
            }

            // We'll cheat a bit here.  The samples happend to be in order and so are our
            // expected values, so we'll just test in order until a failure.  If the
            // implementation changes to return samples in an arbitrary order, this test
            // must change.  There's no actual restriction on the order of the samples.

            for (int j = 0; ok && j < count; ++j ) { // we've verified count < 4
                double val = samples[j];
                if (*ep == 0 || *ep == ';') {
                    errln("got unexpected value[%d]: %g", j, val);
                    ok = FALSE;
                    break;
                }
                char* xp;
                double expectedVal = strtod(ep, &xp);
                if (xp == ep) {
                    // internal error
                    errln("yikes!");
                    ok = FALSE;
                    break;
                }
                ep = xp;
                if (expectedVal != val) {
                    errln("expected %g but got %g", expectedVal, val);
                    ok = FALSE;
                    break;
                }
                if (*ep == ',') ++ep;
            }

            if (ok && count != -1) {
                if (!(*ep == 0 || *ep == ';')) {
                    errln("file: %s, line %d, didn't get expected value: %s", __FILE__, __LINE__, ep);
                    ok = FALSE;
                }
            }

            while (*ep && *ep != ';') ++ep;
            if (*ep == ';') ++ep;
            rp = ep;
        }
    #endif
    delete p;
    }
}

void
PluralRulesTest::testCompactDecimalPluralKeyword() {
    IcuTestErrorCode errorCode(*this, "testCompactDecimalPluralKeyword");

    LocalPointer<PluralRules> rules(PluralRules::createRules(
        u"one: i = 0,1 @integer 0, 1 @decimal 0.0~1.5;  "
        u"many: e = 0 and i % 1000000 = 0 and v = 0 or e != 0 .. 5;  "
        u"other:  @integer 2~17, 100, 1000, 10000, 100000, 1000000,  "
        u"  @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …", errorCode));

    if (U_FAILURE(errorCode)) {
        errln("Couldn't instantiate plurals rules from string, with error = %s", u_errorName(errorCode));
        return;
    }

    const char* localeName = "fr-FR";
    Locale locale = Locale::createFromName(localeName);

    struct TestCase {
        const char16_t* skeleton;
        const int input;
        const char16_t* expectedFormattedOutput;
        const char16_t* expectedPluralRuleKeyword;
    } cases[] = {
        // unlocalized formatter skeleton, input, string output, plural rule keyword
        {u"",             0, u"0", u"one"},
        {u"compact-long", 0, u"0", u"one"},

        {u"",             1, u"1", u"one"},
        {u"compact-long", 1, u"1", u"one"},

        {u"",             2, u"2", u"other"},
        {u"compact-long", 2, u"2", u"other"},

        {u"",             1000000, u"1 000 000", u"many"},
        {u"compact-long", 1000000, u"1 million", u"many"},

        {u"",             1000001, u"1 000 001", u"other"},
        {u"compact-long", 1000001, u"1 million", u"many"},

        {u"",             120000,  u"1 200 000",    u"other"},
        {u"compact-long", 1200000, u"1,2 millions", u"many"},

        {u"",             1200001, u"1 200 001",    u"other"},
        {u"compact-long", 1200001, u"1,2 millions", u"many"},

        {u"",             2000000, u"2 000 000",  u"many"},
        {u"compact-long", 2000000, u"2 millions", u"many"},
    };
    for (const auto& cas : cases) {
        const char16_t* skeleton = cas.skeleton;
        const int input = cas.input;
        const char16_t* expectedPluralRuleKeyword = cas.expectedPluralRuleKeyword;

        UnicodeString actualPluralRuleKeyword =
            getPluralKeyword(rules, locale, input, skeleton);

        UnicodeString message(UnicodeString(localeName) + u" " + DoubleToUnicodeString(input));
        assertEquals(message, expectedPluralRuleKeyword, actualPluralRuleKeyword);
    }
}

UnicodeString PluralRulesTest::getPluralKeyword(const LocalPointer<PluralRules> &rules, Locale locale, double number, const char16_t* skeleton) {
    IcuTestErrorCode errorCode(*this, "getPluralKeyword");
    UnlocalizedNumberFormatter ulnf = NumberFormatter::forSkeleton(skeleton, errorCode);
    if (errorCode.errIfFailureAndReset("PluralRules::getPluralKeyword(<PluralRules>, <locale>, %d, %s) failed", number, skeleton)) {
        return nullptr;
    }
    LocalizedNumberFormatter formatter = ulnf.locale(locale);

    const FormattedNumber fn = formatter.formatDouble(number, errorCode);
    if (errorCode.errIfFailureAndReset("NumberFormatter::formatDouble(%d) failed", number)) {
        return nullptr;
    }

    UnicodeString pluralKeyword = rules->select(fn, errorCode);
    if (errorCode.errIfFailureAndReset("PluralRules->select(FormattedNumber of %d) failed", number)) {
        return nullptr;
    }
    return pluralKeyword;
}

void PluralRulesTest::testOrdinal() {
    IcuTestErrorCode errorCode(*this, "testOrdinal");
    LocalPointer<PluralRules> pr(PluralRules::forLocale("en", UPLURAL_TYPE_ORDINAL, errorCode));
    if (errorCode.errIfFailureAndReset("PluralRules::forLocale(en, UPLURAL_TYPE_ORDINAL) failed")) {
        return;
    }
    UnicodeString keyword = pr->select(2.);
    if (keyword != UNICODE_STRING("two", 3)) {
        dataerrln("PluralRules(en-ordinal).select(2) failed");
    }
}

static const char * END_MARK = "999.999";    // Mark end of varargs data.

void PluralRulesTest::checkSelect(const LocalPointer<PluralRules> &rules, UErrorCode &status,
                                  int32_t line, const char *keyword, ...) {
    // The varargs parameters are a const char* strings, each being a decimal number.
    //   The formatting of the numbers as strings is significant, e.g.
    //     the difference between "2" and "2.0" can affect which rule matches (which keyword is selected).
    // Note: rules parameter is a LocalPointer reference rather than a PluralRules * to avoid having
    //       to write getAlias() at every (numerous) call site.

    if (U_FAILURE(status)) {
        errln("file %s, line %d, ICU error status: %s.", __FILE__, line, u_errorName(status));
        status = U_ZERO_ERROR;
        return;
    }

    if (rules == NULL) {
        errln("file %s, line %d: rules pointer is NULL", __FILE__, line);
        return;
    }

    va_list ap;
    va_start(ap, keyword);
    for (;;) {
        const char *num = va_arg(ap, const char *);
        if (strcmp(num, END_MARK) == 0) {
            break;
        }

        // DigitList is a convenient way to parse the decimal number string and get a double.
        DecimalQuantity  dl;
        dl.setToDecNumber(StringPiece(num), status);
        if (U_FAILURE(status)) {
            errln("file %s, line %d, ICU error status: %s.", __FILE__, line, u_errorName(status));
            status = U_ZERO_ERROR;
            continue;
        }
        double numDbl = dl.toDouble();
        const char *decimalPoint = strchr(num, '.');
        int fractionDigitCount = decimalPoint == NULL ? 0 : static_cast<int>((num + strlen(num) - 1) - decimalPoint);
        int fractionDigits = fractionDigitCount == 0 ? 0 : atoi(decimalPoint + 1);
        FixedDecimal ni(numDbl, fractionDigitCount, fractionDigits);

        UnicodeString actualKeyword = rules->select(ni);
        if (actualKeyword != UnicodeString(keyword)) {
            errln("file %s, line %d, select(%s) returned incorrect keyword. Expected %s, got %s",
                   __FILE__, line, num, keyword, US(actualKeyword).cstr());
        }
    }
    va_end(ap);
}

void PluralRulesTest::testSelect() {
    UErrorCode status = U_ZERO_ERROR;
    LocalPointer<PluralRules> pr(PluralRules::createRules("s: n in 1,3,4,6", status));
    checkSelect(pr, status, __LINE__, "s", "1.0", "3.0", "4.0", "6.0", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "0.0", "2.0", "3.1", "7.0", END_MARK);

    pr.adoptInstead(PluralRules::createRules("s: n not in 1,3,4,6", status));
    checkSelect(pr, status, __LINE__, "other", "1.0", "3.0", "4.0", "6.0", END_MARK);
    checkSelect(pr, status, __LINE__, "s", "0.0", "2.0", "3.1", "7.0", END_MARK);

    pr.adoptInstead(PluralRules::createRules("r: n in 1..4, 7..10, 14 .. 17;"
                                             "s: n is 29;", status));
    checkSelect(pr, status, __LINE__, "r", "1.0", "3.0", "7.0", "8.0", "10.0", "14.0", "17.0", END_MARK);
    checkSelect(pr, status, __LINE__, "s", "29.0", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "28.0", "29.1", END_MARK);

    pr.adoptInstead(PluralRules::createRules("a: n mod 10 is 1;  b: n mod 100 is 0 ", status));
    checkSelect(pr, status, __LINE__, "a", "1", "11", "41", "101", "301.00", END_MARK);
    checkSelect(pr, status, __LINE__, "b", "0", "100", "200.0", "300.", "1000", "1100", "110000", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "0.01", "1.01", "0.99", "2", "3", "99", "102", END_MARK);

    // Rules that end with or without a ';' and with or without trailing spaces.
    //    (There was a rule parser bug here with these.)
    pr.adoptInstead(PluralRules::createRules("a: n is 1", status));
    checkSelect(pr, status, __LINE__, "a", "1", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "2", END_MARK);

    pr.adoptInstead(PluralRules::createRules("a: n is 1 ", status));
    checkSelect(pr, status, __LINE__, "a", "1", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "2", END_MARK);

    pr.adoptInstead(PluralRules::createRules("a: n is 1;", status));
    checkSelect(pr, status, __LINE__, "a", "1", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "2", END_MARK);

    pr.adoptInstead(PluralRules::createRules("a: n is 1 ; ", status));
    checkSelect(pr, status, __LINE__, "a", "1", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "2", END_MARK);

    // First match when rules for different keywords are not disjoint.
    //   Also try spacing variations around ':' and '..'
    pr.adoptInstead(PluralRules::createRules("c: n in 5..15;  b : n in 1..10 ;a:n in 10 .. 20", status));
    checkSelect(pr, status, __LINE__, "a", "20", END_MARK);
    checkSelect(pr, status, __LINE__, "b", "1", END_MARK);
    checkSelect(pr, status, __LINE__, "c", "10", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "0", "21", "10.1", END_MARK);

    // in vs within
    pr.adoptInstead(PluralRules::createRules("a: n in 2..10; b: n within 8..15", status));
    checkSelect(pr, status, __LINE__, "a", "2", "8", "10", END_MARK);
    checkSelect(pr, status, __LINE__, "b", "8.01", "9.5", "11", "14.99", "15", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "1", "7.7", "15.01", "16", END_MARK);

    // OR and AND chains.
    pr.adoptInstead(PluralRules::createRules("a: n in 2..10 and n in 4..12 and n not in 5..7", status));
    checkSelect(pr, status, __LINE__, "a", "4", "8", "9", "10", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "2", "3", "5", "7", "11", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n is 2 or n is 5 or n in 7..11 and n in 11..13", status));
    checkSelect(pr, status, __LINE__, "a", "2", "5", "11", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "3", "4", "6", "8", "10", "12", "13", END_MARK);

    // Number attributes -
    //   n: the number itself
    //   i: integer digits
    //   f: visible fraction digits
    //   t: f with trailing zeros removed.
    //   v: number of visible fraction digits
    //   j: = n if there are no visible fraction digits
    //      != anything if there are visible fraction digits

    pr.adoptInstead(PluralRules::createRules("a: i is 123", status));
    checkSelect(pr, status, __LINE__, "a", "123", "123.0", "123.1", "0123.99", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "124", "122.0", END_MARK);

    pr.adoptInstead(PluralRules::createRules("a: f is 120", status));
    checkSelect(pr, status, __LINE__, "a", "1.120", "0.120", "11123.120", "0123.120", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "1.121", "122.1200", "1.12", "120", END_MARK);

    pr.adoptInstead(PluralRules::createRules("a: t is 12", status));
    checkSelect(pr, status, __LINE__, "a", "1.120", "0.12", "11123.12000", "0123.1200000", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "1.121", "122.1200001", "1.11", "12", END_MARK);

    pr.adoptInstead(PluralRules::createRules("a: v is 3", status));
    checkSelect(pr, status, __LINE__, "a", "1.120", "0.000", "11123.100", "0123.124", ".666", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "1.1212", "122.12", "1.1", "122", "0.0000", END_MARK);

    pr.adoptInstead(PluralRules::createRules("a: v is 0 and i is 123", status));
    checkSelect(pr, status, __LINE__, "a", "123", "123.", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "123.0", "123.1", "123.123", "0.123", END_MARK);

    // The reserved words from the rule syntax will also function as keywords.
    pr.adoptInstead(PluralRules::createRules("a: n is 21; n: n is 22; i: n is 23; f: n is 24;"
                                             "t: n is 25; v: n is 26; w: n is 27; j: n is 28"
                                             , status));
    checkSelect(pr, status, __LINE__, "other", "20", "29", END_MARK);
    checkSelect(pr, status, __LINE__, "a", "21", END_MARK);
    checkSelect(pr, status, __LINE__, "n", "22", END_MARK);
    checkSelect(pr, status, __LINE__, "i", "23", END_MARK);
    checkSelect(pr, status, __LINE__, "f", "24", END_MARK);
    checkSelect(pr, status, __LINE__, "t", "25", END_MARK);
    checkSelect(pr, status, __LINE__, "v", "26", END_MARK);
    checkSelect(pr, status, __LINE__, "w", "27", END_MARK);
    checkSelect(pr, status, __LINE__, "j", "28", END_MARK);

    pr.adoptInstead(PluralRules::createRules("not: n=31; and: n=32; or: n=33; mod: n=34;"
                                             "in: n=35; within: n=36;is:n=37"
                                             , status));
    checkSelect(pr, status, __LINE__, "other",  "30", "39", END_MARK);
    checkSelect(pr, status, __LINE__, "not",    "31", END_MARK);
    checkSelect(pr, status, __LINE__, "and",    "32", END_MARK);
    checkSelect(pr, status, __LINE__, "or",     "33", END_MARK);
    checkSelect(pr, status, __LINE__, "mod",    "34", END_MARK);
    checkSelect(pr, status, __LINE__, "in",     "35", END_MARK);
    checkSelect(pr, status, __LINE__, "within", "36", END_MARK);
    checkSelect(pr, status, __LINE__, "is",     "37", END_MARK);

// Test cases from ICU4J PluralRulesTest.parseTestData

    pr.adoptInstead(PluralRules::createRules("a: n is 1", status));
    checkSelect(pr, status, __LINE__, "a", "1", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 10 is 2", status));
    checkSelect(pr, status, __LINE__, "a", "2", "12", "22", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n is not 1", status));
    checkSelect(pr, status, __LINE__, "a", "0", "2", "3", "4", "5", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 3 is not 1", status));
    checkSelect(pr, status, __LINE__, "a", "0", "2", "3", "5", "6", "8", "9", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n in 2..5", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n within 2..5", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n not in 2..5", status));
    checkSelect(pr, status, __LINE__, "a", "0", "1", "6", "7", "8", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n not within 2..5", status));
    checkSelect(pr, status, __LINE__, "a", "0", "1", "6", "7", "8", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 10 in 2..5", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", "12", "13", "14", "15", "22", "23", "24", "25", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 10 within 2..5", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", "12", "13", "14", "15", "22", "23", "24", "25", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 10 is 2 and n is not 12", status));
    checkSelect(pr, status, __LINE__, "a", "2", "22", "32", "42", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 10 in 2..3 or n mod 10 is 5", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "5", "12", "13", "15", "22", "23", "25", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 10 within 2..3 or n mod 10 is 5", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "5", "12", "13", "15", "22", "23", "25", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n is 1 or n is 4 or n is 23", status));
    checkSelect(pr, status, __LINE__, "a", "1", "4", "23", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 2 is 1 and n is not 3 and n in 1..11", status));
    checkSelect(pr, status, __LINE__, "a", "1", "5", "7", "9", "11", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 2 is 1 and n is not 3 and n within 1..11", status));
    checkSelect(pr, status, __LINE__, "a", "1", "5", "7", "9", "11", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 2 is 1 or n mod 5 is 1 and n is not 6", status));
    checkSelect(pr, status, __LINE__, "a", "1", "3", "5", "7", "9", "11", "13", "15", "16", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n in 2..5; b: n in 5..8; c: n mod 2 is 1", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", END_MARK);
    checkSelect(pr, status, __LINE__, "b", "6", "7", "8", END_MARK);
    checkSelect(pr, status, __LINE__, "c", "1", "9", "11", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n within 2..5; b: n within 5..8; c: n mod 2 is 1", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", END_MARK);
    checkSelect(pr, status, __LINE__, "b", "6", "7", "8", END_MARK);
    checkSelect(pr, status, __LINE__, "c", "1", "9", "11", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n in 2, 4..6; b: n within 7..9,11..12,20", status));
    checkSelect(pr, status, __LINE__, "a", "2", "4", "5", "6", END_MARK);
    checkSelect(pr, status, __LINE__, "b", "7", "8", "9", "11", "12", "20", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n in 2..8, 12 and n not in 4..6", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "7", "8", "12", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n mod 10 in 2, 3,5..7 and n is not 12", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "5", "6", "7", "13", "15", "16", "17", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a: n in 2..6, 3..7", status));
    checkSelect(pr, status, __LINE__, "a", "2", "3", "4", "5", "6", "7", END_MARK);

    // Extended Syntax, with '=', '!=' and '%' operators.
    pr.adoptInstead(PluralRules::createRules("a: n = 1..8 and n!= 2,3,4,5", status));
    checkSelect(pr, status, __LINE__, "a", "1", "6", "7", "8", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "0", "2", "3", "4", "5", "9", END_MARK);
    pr.adoptInstead(PluralRules::createRules("a:n % 10 != 1", status));
    checkSelect(pr, status, __LINE__, "a", "2", "6", "7", "8", END_MARK);
    checkSelect(pr, status, __LINE__, "other", "1", "21", "211", "91", END_MARK);
}

void PluralRulesTest::testSelectRange() {
    IcuTestErrorCode status(*this, "testSelectRange");

    int32_t d1 = 102;
    int32_t d2 = 201;
    Locale locale("sl");

    // Locale sl has interesting data: one + two => few
    auto range = NumberRangeFormatter::withLocale(locale).formatFormattableRange(d1, d2, status);
    auto rules = LocalPointer<PluralRules>(PluralRules::forLocale(locale, status), status);
    if (status.errIfFailureAndReset()) {
        return;
    }

    // For testing: get plural form of first and second numbers
    auto a = NumberFormatter::withLocale(locale).formatDouble(d1, status);
    auto b = NumberFormatter::withLocale(locale).formatDouble(d2, status);
    assertEquals("First plural", u"two", rules->select(a, status));
    assertEquals("Second plural", u"one", rules->select(b, status));

    // Check the range plural now:
    auto form = rules->select(range, status);
    assertEquals("Range plural", u"few", form);

    // Test after copying:
    PluralRules copy(*rules);
    form = copy.select(range, status);
    assertEquals("Range plural after copying", u"few", form);

    // Test when plural ranges data is unavailable:
    auto bare = LocalPointer<PluralRules>(
        PluralRules::createRules(u"a: i = 0,1", status), status);
    if (status.errIfFailureAndReset()) { return; }
    form = bare->select(range, status);
    status.expectErrorAndReset(U_UNSUPPORTED_ERROR);

    // However, they should not set an error when no data is available for a language.
    auto xyz = LocalPointer<PluralRules>(
        PluralRules::forLocale("xyz", status));
    form = xyz->select(range, status);
    assertEquals("Fallback form", u"other", form);
}

void PluralRulesTest::testAvailbleLocales() {

    // Hash set of (char *) strings.
    UErrorCode status = U_ZERO_ERROR;
    UHashtable *localeSet = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, uhash_compareLong, &status);
    uhash_setKeyDeleter(localeSet, uprv_deleteUObject);
    if (U_FAILURE(status)) {
        errln("file %s,  line %d: Error status = %s", __FILE__, __LINE__, u_errorName(status));
        return;
    }

    // Check that each locale returned by the iterator is unique.
    StringEnumeration *localesEnum = PluralRules::getAvailableLocales(status);
    int localeCount = 0;
    for (;;) {
        const char *locale = localesEnum->next(NULL, status);
        if (U_FAILURE(status)) {
            dataerrln("file %s,  line %d: Error status = %s", __FILE__, __LINE__, u_errorName(status));
            return;
        }
        if (locale == NULL) {
            break;
        }
        localeCount++;
        int32_t oldVal = uhash_puti(localeSet, new UnicodeString(locale), 1, &status);
        if (oldVal != 0) {
            errln("file %s,  line %d: locale %s was seen before.", __FILE__, __LINE__, locale);
        }
    }

    // Reset the iterator, verify that we get the same count.
    localesEnum->reset(status);
    int32_t localeCount2 = 0;
    while (localesEnum->next(NULL, status) != NULL) {
        if (U_FAILURE(status)) {
            errln("file %s,  line %d: Error status = %s", __FILE__, __LINE__, u_errorName(status));
            break;
        }
        localeCount2++;
    }
    if (localeCount != localeCount2) {
        errln("file %s,  line %d: locale counts differ. They are (%d, %d)",
            __FILE__, __LINE__, localeCount, localeCount2);
    }

    // Instantiate plural rules for each available locale.
    localesEnum->reset(status);
    for (;;) {
        status = U_ZERO_ERROR;
        const char *localeName = localesEnum->next(NULL, status);
        if (U_FAILURE(status)) {
            errln("file %s,  line %d: Error status = %s, locale = %s",
                __FILE__, __LINE__, u_errorName(status), localeName);
            return;
        }
        if (localeName == NULL) {
            break;
        }
        Locale locale = Locale::createFromName(localeName);
        PluralRules *pr = PluralRules::forLocale(locale, status);
        if (U_FAILURE(status)) {
            errln("file %s,  line %d: Error %s creating plural rules for locale %s",
                __FILE__, __LINE__, u_errorName(status), localeName);
            continue;
        }
        if (pr == NULL) {
            errln("file %s, line %d: Null plural rules for locale %s", __FILE__, __LINE__, localeName);
            continue;
        }

        // Pump some numbers through the plural rules.  Can't check for correct results,
        // mostly this to tickle any asserts or crashes that may be lurking.
        for (double n=0; n<120.0; n+=0.5) {
            UnicodeString keyword = pr->select(n);
            if (keyword.length() == 0) {
                errln("file %s, line %d, empty keyword for n = %g, locale %s",
                    __FILE__, __LINE__, n, localeName);
            }
        }
        delete pr;
    }

    uhash_close(localeSet);
    delete localesEnum;

}

void PluralRulesTest::testParseErrors() {
    // Test rules with syntax errors.
    // Creation of PluralRules from them should fail.

    static const char *testCases[] = {
            "a: n mod 10, is 1",
            "a: q is 13",
            "a  n is 13",
            "a: n is 13,",
            "a: n is 13, 15,   b: n is 4",
            "a: n is 1, 3, 4.. ",
            "a: n within 5..4",
            "A: n is 13",          // Uppercase keywords not allowed.
            "a: n ! = 3",          // spaces in != operator
            "a: n = not 3",        // '=' not exact equivalent of 'is'
            "a: n ! in 3..4"       // '!' not exact equivalent of 'not'
            "a: n % 37 ! in 3..4"

            };
    for (int i=0; i<UPRV_LENGTHOF(testCases); i++) {
        const char *rules = testCases[i];
        UErrorCode status = U_ZERO_ERROR;
        PluralRules *pr = PluralRules::createRules(UnicodeString(rules), status);
        if (U_SUCCESS(status)) {
            errln("file %s, line %d, expected failure with \"%s\".", __FILE__, __LINE__, rules);
        }
        if (pr != NULL) {
            errln("file %s, line %d, expected NULL. Rules: \"%s\"", __FILE__, __LINE__, rules);
        }
    }
    return;
}

void PluralRulesTest::testFixedDecimal() {
    struct DoubleTestCase {
        double n;
        int32_t fractionDigitCount;
        int64_t fractionDigits;
    };

    // Check that the internal functions for extracting the decimal fraction digits from
    //   a double value are working.
    static DoubleTestCase testCases[] = {
        {1.0, 0, 0},
        {123456.0, 0, 0},
        {1.1, 1, 1},
        {1.23, 2, 23},
        {1.234, 3, 234},
        {1.2345, 4, 2345},
        {1.23456, 5, 23456},
        {.1234, 4, 1234},
        {.01234, 5, 1234},
        {.001234, 6, 1234},
        {.0001234, 7, 1234},
        {100.1234, 4, 1234},
        {100.01234, 5, 1234},
        {100.001234, 6, 1234},
        {100.0001234, 7, 1234}
    };

    for (int i=0; i<UPRV_LENGTHOF(testCases); ++i) {
        DoubleTestCase &tc = testCases[i];
        int32_t numFractionDigits = FixedDecimal::decimals(tc.n);
        if (numFractionDigits != tc.fractionDigitCount) {
            errln("file %s, line %d: decimals(%g) expected %d, actual %d",
                   __FILE__, __LINE__, tc.n, tc.fractionDigitCount, numFractionDigits);
            continue;
        }
        int64_t actualFractionDigits = FixedDecimal::getFractionalDigits(tc.n, numFractionDigits);
        if (actualFractionDigits != tc.fractionDigits) {
            errln("file %s, line %d: getFractionDigits(%g, %d): expected %ld, got %ld",
                  __FILE__, __LINE__, tc.n, numFractionDigits, tc.fractionDigits, actualFractionDigits);
        }
    }
}

void PluralRulesTest::testSelectTrailingZeros() {
    IcuTestErrorCode status(*this, "testSelectTrailingZeros");
    number::UnlocalizedNumberFormatter unf = number::NumberFormatter::with()
            .precision(number::Precision::fixedFraction(2));
    struct TestCase {
        const char* localeName;
        const char16_t* expectedDoubleKeyword;
        const char16_t* expectedFormattedKeyword;
        double number;
    } cases[] = {
        {"bs",  u"few",   u"other", 5.2},  // 5.2 => two, but 5.20 => other
        {"si",  u"one",   u"one",   0.0},
        {"si",  u"one",   u"one",   1.0},
        {"si",  u"one",   u"other", 0.1},  // 0.1 => one, but 0.10 => other
        {"si",  u"one",   u"one",   0.01}, // 0.01 => one
        {"hsb", u"few",   u"few",   1.03}, // (f % 100 == 3) => few
        {"hsb", u"few",   u"other", 1.3},  // 1.3 => few, but 1.30 => other
    };
    for (const auto& cas : cases) {
        UnicodeString message(UnicodeString(cas.localeName) + u" " + DoubleToUnicodeString(cas.number));
        status.setScope(message);
        Locale locale(cas.localeName);
        LocalPointer<PluralRules> rules(PluralRules::forLocale(locale, status));
        if (U_FAILURE(status)) {
            dataerrln("Failed to create PluralRules by PluralRules::forLocale(%s): %s\n",
                      cas.localeName, u_errorName(status));
            return;
        }
        assertEquals(message, cas.expectedDoubleKeyword, rules->select(cas.number));
        number::FormattedNumber fn = unf.locale(locale).formatDouble(cas.number, status);
        assertEquals(message, cas.expectedFormattedKeyword, rules->select(fn, status));
        status.errIfFailureAndReset();
    }
}

void PluralRulesTest::compareLocaleResults(const char* loc1, const char* loc2, const char* loc3) {
    UErrorCode status = U_ZERO_ERROR;
    LocalPointer<PluralRules> rules1(PluralRules::forLocale(loc1, status));
    LocalPointer<PluralRules> rules2(PluralRules::forLocale(loc2, status));
    LocalPointer<PluralRules> rules3(PluralRules::forLocale(loc3, status));
    if (U_FAILURE(status)) {
        dataerrln("Failed to create PluralRules for one of %s, %s, %s: %s\n", loc1, loc2, loc3, u_errorName(status));
        return;
    }
    for (int32_t value = 0; value <= 12; value++) {
        UnicodeString result1 = rules1->select(value);
        UnicodeString result2 = rules2->select(value);
        UnicodeString result3 = rules3->select(value);
        if (result1 != result2 || result1 != result3) {
            errln("PluralRules.select(%d) does not return the same values for %s, %s, %s\n", value, loc1, loc2, loc3);
        }
    }
}

void PluralRulesTest::testLocaleExtension() {
    IcuTestErrorCode errorCode(*this, "testLocaleExtension");
    LocalPointer<PluralRules> rules(PluralRules::forLocale("pt@calendar=gregorian", errorCode));
    if (errorCode.errIfFailureAndReset("PluralRules::forLocale()")) { return; }
    UnicodeString key = rules->select(1);
    assertEquals("pt@calendar=gregorian select(1)", u"one", key);
    compareLocaleResults("ar", "ar_SA", "ar_SA@calendar=gregorian");
    compareLocaleResults("ru", "ru_UA", "ru-u-cu-RUB");
    compareLocaleResults("fr", "fr_CH", "fr@ms=uksystem");
}

#endif /* #if !UCONFIG_NO_FORMATTING */


text:
//---------------------------------------------------------------------
// <copyright file="ObjectModelExtensions.cs" company="Microsoft">
//      Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
//---------------------------------------------------------------------

namespace Microsoft.Test.Taupo.OData.Writer.Tests.Common
{
    #region Namespaces
    using Microsoft.OData.Core;
    using Microsoft.Test.Taupo.Common;
    #endregion Namespaces

    /// <summary>
    /// Extension methods for OData OM.
    /// </summary>
    public static class ObjectModelExtensions
    {
        /// <summary>
        /// Sets annotation on OData item.
        /// </summary>
        /// <typeparam name="TItem">The type of the OData item to set the annotation on.</typeparam>
        /// <typeparam name="TAnnotation">The type of the annotation to set.</typeparam>
        /// <param name="item">The OData item to set the annotation on.</param>
        /// <param name="annotation">The annotation to set.</param>
        /// <returns>The <paramref name="item"/> with the annotation set.</returns>
        public static TItem WithAnnotation<TItem, TAnnotation>(this TItem item, TAnnotation annotation)
            where TItem : ODataItem
            where TAnnotation : class
        {
            ExceptionUtilities.CheckArgumentNotNull(item, "item");
            item.SetAnnotation(annotation);
            return item;
        }
    }
}

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="latest_ob.xsl" type="text/xsl"?>
<current_observation version="1.0"
	 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	 xsi:noNamespaceSchemaLocation="http://www.weather.gov/view/current_observation.xsd">
	<credit>NOAA's National Weather Service</credit>
	<credit_URL>http://weather.gov/</credit_URL>
	<image>
		<url>http://weather.gov/images/xml_logo.gif</url>
		<title>NOAA's National Weather Service</title>
		<link>http://weather.gov</link>
	</image>
	<suggested_pickup>15 minutes after the hour</suggested_pickup>
	<suggested_pickup_period>60</suggested_pickup_period>
	<location>Unknown Station</location>
	<station_id>SH302</station_id>
	<observation_time>Last Updated on Jan 4 2016, 8:00 pm AST</observation_time>
        <observation_time_rfc822>Mon, 04 Jan 2016 20:00:00 -0400</observation_time_rfc822>
	<temperature_string>32.0 F (0.0 C)</temperature_string>
	<temp_f>32.0</temp_f>
	<temp_c>0.0</temp_c>
	<water_temp_f>32.0</water_temp_f>
	<water_temp_c>0.0</water_temp_c>
	<wind_string>Northwest at 3.4 MPH (2.92 KT)</wind_string>
	<wind_dir>Northwest</wind_dir>
	<wind_degrees>330</wind_degrees>
	<wind_mph>3.4</wind_mph>
	<wind_gust_mph>0.0</wind_gust_mph>
	<wind_kt>2.92</wind_kt>
	<pressure_string>1024.0 mb</pressure_string>
	<pressure_mb>1024.0</pressure_mb>
	<pressure_tendency_mb>0.0</pressure_tendency_mb>
	<dewpoint_string>27.0 F (-2.8 C)</dewpoint_string>
	<dewpoint_f>27.0</dewpoint_f>
	<dewpoint_c>-2.8</dewpoint_c>
	<visibility_mi>11.00</visibility_mi>
	<dominant_period_sec>0</dominant_period_sec>
	<average_period_sec>0.0</average_period_sec>
	<mean_wave_dir>North</mean_wave_dir>
	<mean_wave_degrees>0</mean_wave_degrees>
	<disclaimer_url>http://weather.gov/disclaimer.html</disclaimer_url>
	<copyright_url>http://weather.gov/disclaimer.html</copyright_url>
	<privacy_policy_url>http://weather.gov/notice.html</privacy_policy_url>
</current_observation>


text:
package com.opengamma.sesame.graph.convert;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.joda.convert.StringConverter;

import com.opengamma.sesame.function.ParameterUtils;
import com.opengamma.util.ArgumentChecker;

@SuppressWarnings("unchecked")
abstract class CollectionConverter implements StringConverter<Object> {

  private final StringConverter<Object> _elementConverter;

  CollectionConverter(StringConverter<Object> elementConverter) {
    _elementConverter = ArgumentChecker.notNull(elementConverter, "elementConverter");
  }

  @Override
  public Collection<?> convertFromString(Class<?> type, String value) {
    Collection<String> stringValues = StringParser.parse(value);
    Collection<Object> convertedValues = createEmptyCollection(stringValues.size());

    for (String stringValue : stringValues) {
      convertedValues.add(_elementConverter.convertFromString(type, stringValue));
    }
    return convertedValues;
  }

  protected abstract Collection<Object> createEmptyCollection(int size);

  @Override
  public String convertToString(Object value) {
    ArgumentChecker.notNull(value, "value");

    if (!(value instanceof Collection<?>)) {
      throw new IllegalArgumentException("value must be a collection. " + value);
    }
    Collection<Object> list = (Collection<Object>) value;
    List<String> convertedElements = new ArrayList<>(list.size());

    for (Object item : list) {
      convertedElements.add(ParameterUtils.escapeString(_elementConverter.convertToString(item)));
    }
    return StringUtils.join(convertedElements, " ");
  }

  public static StringConverter<Object> create(Class<?> collectionType, StringConverter<Object> elementConverter) {
    if (collectionType == List.class) {
      return new ListConverter(elementConverter);
    }
    if (collectionType == Set.class) {
      return new SetConverter(elementConverter);
    }
    throw new IllegalArgumentException("Unexpected collection type " + collectionType);
  }

  private static class SetConverter extends CollectionConverter {

    /* package */ SetConverter(StringConverter<Object> elementConverter) {
      super(elementConverter);
    }

    @Override
    protected Collection<Object> createEmptyCollection(int size) {
      return new HashSet<>(size);
    }
  }

  private static class ListConverter extends CollectionConverter {

    /* package */ ListConverter(StringConverter<Object> elementConverter) {
      super(elementConverter);
    }

    @Override
    protected Collection<Object> createEmptyCollection(int size) {
      return new ArrayList<>(size);
    }
  }
}


text:
package org.simpleflatmapper.converter.joda.impl;

import org.joda.time.LocalDateTime;
import org.simpleflatmapper.converter.Context;
import org.simpleflatmapper.converter.ContextualConverter;

import java.util.Date;

public class DateToJodaLocalDateTimeConverter implements ContextualConverter<Date, LocalDateTime> {
    @Override
    public LocalDateTime convert(Date in, Context context) throws Exception {
        if (in == null) return null;
        return LocalDateTime.fromDateFields(in);
    }
}


text:
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateGrnsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('grns', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('inspection_note_id', false, true);
            $table->string('grn_number', 50)->unique();
            $table->date('grn_date');
            $table->integer('indigeneous',false,true)->default(0);
            $table->integer('imported',false,true)->default(0);
            $table->string('cn_number',50)->nullable();
            $table->string('invoice_number',127)->nullable();
            $table->date('invoice_date')->nullable();

            $table->integer('prepared_by',false, true);
            $table->date('prepared_date');

            $table->integer('inspected_by',false, true)->nullable();
            $table->string('inspection_reference_number')->nullable();
            $table->date('inspection_date')->nullable();;

            $table->integer('checked_by',false, true)->nullable();
            $table->date('check_date')->nullable();

            $table->integer('forwarded_by',false, true)->nullable();
            $table->date('forward_date')->nullable();

            $table->integer('authorised_for_stock_charging',false, true)->nullable();
            $table->date('authorised_for_stock_charging_date')->nullable();

            $table->integer('stock_taken_by',false, true)->nullable();
            $table->date('stock_taken_date')->nullable();

            $table->integer('valued_by',false, true)->nullable();
            $table->date('valued_date')->nullable();

            $table->integer('accounts_checked_by',false, true)->nullable();
            $table->date('accounts_check_date')->nullable();

            $table->integer('ledger_posted_by',false, true)->nullable();
            $table->date('ledger_posted_date')->nullable();

            $table->string('current_status')->default('grn_prepared');

            $table->timestamps();

            $table->foreign('inspection_note_id')->references('id')->on('inspection_notes');
            $table->foreign('prepared_by')->references('id')->on('material_users');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('grns');
    }
}


text:
default: lint

lint:
	npm test


text:
import ConfigParser
import random
import string
from twython import Twython

print
print "==============================================================="
print "                   ~  CONFIGURE TWITCHER  ~"
print "          Please complete the following instructions."
print "==============================================================="
print
print

Config = ConfigParser.ConfigParser()

SECRET_KEY = ''.join([random.SystemRandom().choice(
    "{}{}{}".format(string.ascii_letters, string.digits, string.punctuation)) for i in range(50)])

SECRET_KEY = SECRET_KEY.replace('%', '_')

# WRITE DJANGO SECRET
Config.add_section('DJANGO')
Config.set('DJANGO', 'SECRET_KEY', SECRET_KEY)

print
print "==============================================================="
print "Please go to https://apps.twitter.com and create a new app now."
print "After creating, please complete the following instructions."
print "==============================================================="
print
print

TWITTER_APP_KEY = raw_input('Enter your API key -> ')
TWITTER_APP_SECRET = raw_input('Enter your API secret -> ')

twitter = Twython(TWITTER_APP_KEY, TWITTER_APP_SECRET)
auth = twitter.get_authentication_tokens()

TEMP_TWITTER_OAUTH_TOKEN = auth['oauth_token']
TEMP_TWITTER_OAUTH_TOKEN_SECRET = auth['oauth_token_secret']

print
print "==============================================================="
print "Please authenticate the app by clicking the following url -> "
print "%s" % auth['auth_url']
print
print "          You will receive a PIN CODE. Enter below."
print "==============================================================="
print
print

twitter = Twython(TWITTER_APP_KEY, TWITTER_APP_SECRET,
                  TEMP_TWITTER_OAUTH_TOKEN, TEMP_TWITTER_OAUTH_TOKEN_SECRET)

OAUTH_VERIFIER = raw_input('Enter your PIN CODE -> ')
final_step = twitter.get_authorized_tokens(OAUTH_VERIFIER)

TWITTER_OAUTH_TOKEN = final_step['oauth_token']
TWITTER_OAUTH_TOKEN_SECRET = final_step['oauth_token_secret']

# WRITE TWITTER SECRETS
Config.add_section('TWITTER_API')
Config.set('TWITTER_API', 'TWITTER_APP_KEY', TWITTER_APP_KEY)
Config.set('TWITTER_API', 'TWITTER_APP_SECRET', TWITTER_APP_SECRET)
Config.set('TWITTER_API', 'TWITTER_OAUTH_TOKEN', TWITTER_OAUTH_TOKEN)
Config.set('TWITTER_API', 'TWITTER_OAUTH_TOKEN_SECRET', TWITTER_OAUTH_TOKEN_SECRET)

# CREATE THE CONFIG FILE
config_file = open('config.ini', 'w')
Config.write(config_file)
config_file.close()


text:
layout:     	post
officer:        true
name:    	 	Edgar Rios
position: 	Historian
hometown: 	Mexico City
alma_mater: 	Monterrey Institute of Technology
advisor: 		Alex Bui
header-img: 	"img/officers/edgar.jpg"
active: 		false
year: 2015
email:
department: 	Bioengineering
---

#Bio and Interests:
Research interests: Medical image analysis, MRI, brain tumor analysis (GBM), medical informatics.

Spare time: Movies, videogames, food, exercise, explore the world

#eGSA Mission Statement:
Promote and engage engineering students on the different eGSA events and activities.
Create and strengthen links between UCLA students and affiliated companies.

#Past eGSA Position(s)


text:
<?php

class Squareup_Omni_Model_Inventory extends Squareup_Omni_Model_Square
{
    /**
     * Init class
     */
    public function _construct()
    {
        parent::_construct();
        $this->_init('squareup_omni/inventory');
    }

    public function start()
    {
        if (Squareup_Omni_Model_System_Config_Source_Options_Records::SQUARE == $this->_config->getSor()) {
            Mage::getModel('squareup_omni/inventory_import')->start();
        } else {
            Mage::getModel('squareup_omni/inventory_export')->start();
        }

        return true;
    }

}

/* Filename: Inventory.php */
/* Location: app/code/community/Squareup/Omni/Model/Inventory.php */


text:
<?php

/**
 * ArchiveTable
 *
 * This class has been auto-generated by the Doctrine ORM Framework
 */
class ArchiveTable extends Doctrine_Table {

    /**
     * Returns an instance of this class.
     *
     * @return object ArchiveTable
     */
    public static function getInstance() {
        return Doctrine_Core::getTable('Archive');
    }

    public function getArchivesByMotif($motif) {

        $q = $this->createQuery('a')
                        ->where('a.motifDepart =?', $motif);
        return $q;
    }

    public function getArchivesSortedById(){
        $q = $this->createQuery('a')
                ->orderBy('a.id DESC');
        return $q;
    }

}


text:
package com.rodolfonavalon.shaperipplelibrary;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.os.Bundle;

import static com.rodolfonavalon.shaperipplelibrary.DebugLogger.logD;
import static com.rodolfonavalon.shaperipplelibrary.DebugLogger.logE;

/**
 * This is a controller for ICE_CREAM_SANDWICH and up, where is handles the activity life cycle.
 * Each call to {@link Activity#onPause()} will stop the ripple and restart it when it call the
 * {@link Activity#onResume()}.
 * <p>
 * We make sure that the listener is detached when activity has been destroyed.s
 */
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
class LifeCycleManager implements Application.ActivityLifecycleCallbacks {

    private ShapeRipple shapeRipple;
    private Activity activity;

    LifeCycleManager(ShapeRipple shapeRipple) {
        this.shapeRipple = shapeRipple;
    }

    void attachListener() {
        if (shapeRipple == null) {
            logE("Shape Ripple is null, activity listener is not attached!!");
            return;
        }

        activity = getActivity(shapeRipple.getContext());
        activity.getApplication().registerActivityLifecycleCallbacks(this);
    }

    private void detachListener() {
        if (activity == null) {
            return;
        }

        activity.getApplication().unregisterActivityLifecycleCallbacks(this);
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}

    @Override
    public void onActivityStarted(Activity activity) {}

    @Override
    public void onActivityResumed(Activity activity) {
        if (shapeRipple == null || this.activity != activity) {
            return;
        }

        shapeRipple.restartRipple();
        logD("Activity is Resumed");
    }

    @Override
    public void onActivityPaused(Activity activity) {
        if (shapeRipple == null || this.activity != activity) {
            return;
        }

        shapeRipple.stop();
        logD("Activity is Paused");
    }

    @Override
    public void onActivityStopped(Activity activity) {}

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}

    @Override
    public void onActivityDestroyed(Activity activity) {
        if (this.activity != activity) {
            return;
        }

        detachListener();
        logD("Activity is Destroyed");
    }

    private Activity getActivity(Context context) {
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                return (Activity) context;
            }
            context = ((ContextWrapper) context).getBaseContext();
        }

        throw new IllegalArgumentException("Context does not derived from any activity, Do not use the Application Context!!");
    }
}


text:
package com.appeligo.channelfeed.work;

import com.knowbout.cc2nlp.CCEventService;
import com.knowbout.cc2nlp.CCSentenceEvent;
import com.knowbout.cc2nlp.CCXDSEvent;
import com.knowbout.cc2nlp.CaptionTypeChangeEvent;
import com.knowbout.cc2nlp.ITVLinkEvent;
import com.knowbout.cc2nlp.ProgramStartEvent;

public class NullCCEventService implements CCEventService {
	public String captureSentence(CCSentenceEvent event) {
		return "SUCCESS";
	}

	public String captionTypeChanged(CaptionTypeChangeEvent captionTypeChangedEvent) {
		return "SUCCESS";
	}

	public String captureITVLink(ITVLinkEvent itvLinkEvent) {
		return "SUCCESS";
	}

	public String startProgram(ProgramStartEvent programStartEvent) {
		return "SUCCESS";
	}

	public String captureXDS(CCXDSEvent xdsEvent) {
		return "SUCCESS";
	}
}


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="theme-color" content="#48c9b0">
    <meta name="author" content="TauDuluan.id">
    <title>Tau Duluan</title>

    <link href="css/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">

    <link href="css/flat-ui.min.css" rel="stylesheet">

    <link rel="shortcut icon" href="img/favicon.ico">
    <link rel="stylesheet" type="text/css" href="css/style.css">

    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar">
      Toggle navigation
    </button>
    TauDuluan

Home

Trending

Artis

Hot News

Populer

Wawancara

    <form class="navbar-form navbar-right" action="" role="search">

          <input class="form-control" id="navbarInput-01" type="search" placeholder="Search">

            <button type="submit" class="btn"></button>

      <button class="btn btn-primary navbar-btn" type="button">LOGIN or REGISTER</button>
    </form>

Syahnaz Sadiqah

Presenter dan Aktris

Ads

Restu Ibu Membawa Kebahagiaan Bagi Raffi Ahmad

"Keberuntungan", itulah yang pantas disematkan kepada Syahnaz. Bagi anda yang jarang
          melihat tayangan televisi bisa dipastikan anda kurang mengenal sosok selebriti satu ini. Syahnaz
          sendiri adalah adik kandung dari Raffi Ahmad, yang kemudian menjelma menjadi seorang
          selebriti karena support sang Kakak yang kerap dipanggilnya Aa.

          Tahun 2013 adalah awal karir Syahnaz, bergabung dengan sang kakak menjadi host di sebuah
          acara musik, kemudian terlibat asmara bersama Billy adiknya alm. Olga Syahputra semakin
          membuat Syahnaz dikenal publik namun sayang hubungan itu kandas meski fans mereka sampai
          saat  ini masih menginginkan kebersamaan mereka.

          Syahnaz sempat merasa tidak nyaman dengan kabar yang mengatakan dia mendompleng nama
          besar sang Kakak, namun seiring waktu Syahnaz akhirnya bisa melepaskan diri dari bayang
          -bayang sang Kakak, tampil bersama Nizam disalah satu televise swasta membuat Syahnaz
          semakin dikenal public dan sampai kini Syahnazz semakin mengasah kemampuannya untuk bisa
          terus eksis di dunia intertainment.

          Tahun 2014 Syahnaz semakin bersinar dikarenakan sinetron 7 manusia harimau. Sinetron ini
          memiliki rating yang tinggi secara otomatis mengangkat nama-nama pemain sinetronnya that
          why I tell you Syahnaz adalah Lucky Girl. Ehmm ada kabar apa ya di tahun 2016 ini ? Selain
          Syahnaz semakin akrab dengan sang pacar ? Yuk kita tunggu kesuksesan lainya dari Idola baru kita ini.

        <button class="btn btn-share facebook"> Share</button>
        <button class="btn btn-share twitter"> Share</button>

          Copyright © TauDuluan.id 2016

          About |
          Advertise |
          Contact |
          FAQ


text:
define(function(require) {
  'use strict';

  var SearchView = require('foreground/view/search/searchView');
  var LayoutType = require('common/enum/layoutType');

  var SearchRegion = Marionette.Region.extend({
    settings: null,

    initialize: function(options) {
      this.settings = options.settings;

      this.listenTo(StreamusFG.channels.search.commands, 'show:search', this._showSearch);
      this.listenTo(StreamusFG.channels.search.commands, 'hide:search', this._hideSearch);
      this.listenTo(StreamusFG.channels.foregroundArea.vent, 'rendered', this._onForegroundAreaRendered);
      this.listenTo(StreamusFG.channels.foregroundArea.vent, 'idle', this._onForegroundAreaIdle);
      this.listenTo(this.settings, 'change:layoutType', this._onSettingsChangeLayoutType);
    },

    _onForegroundAreaRendered: function() {
      this._setWidth(this.settings.get('layoutType'));
      var search = StreamusFG.backgroundProperties.search;

      if (search.hasQuery()) {
        this._createSearchView();

        this._showSearch({
          instant: true
        });
      }
    },

    _onForegroundAreaIdle: function() {
      // If the search view isn't going to be shown right off the bat then it's OK to defer loading until idle so that
      // the initial load time of the application isn't impacted.
      var search = StreamusFG.backgroundProperties.search;
      if (!search.hasQuery()) {
        this._createSearchView();
      }
    },

    _onSettingsChangeLayoutType: function(settings, layoutType) {
      this._setWidth(layoutType);
    },

    _createSearchView: function() {
      if (!this.hasView()) {
        var searchView = new SearchView({
          model: StreamusFG.backgroundProperties.search,
          collection: StreamusFG.backgroundProperties.search.get('results'),
          streamItems: StreamusFG.backgroundProperties.stream.get('items'),
          signInManager: StreamusFG.backgroundProperties.signInManager
        });

        this.show(searchView);
        this.listenTo(searchView, 'hide:search', this._hideSearch);
      }
    },

    _showSearch: function(options) {
      // It's technially possible for the user to request search to be shown before the view has been created.
      // If the Application hasn't entered 'idle' state yet and the user moves fast then they can get here.
      if (!this.hasView()) {
        this._createSearchView();
      }

      // If the view should be visible when UI first loads then do not transition.
      if (options && options.instant) {
        this.$el.addClass('is-instant');
      }

      this.$el.addClass('is-visible');
      this.currentView.triggerMethod('visible');
    },

    _hideSearch: function() {
      this.$el.removeClass('is-instant is-visible');
    },

    _setWidth: function(layoutType) {
      // TODO: How can I safely get the reference to this region's element?
      var element = this.$el.length > 0 ? this.$el : $(this.el);

      element.toggleClass('is-fullWidth', layoutType === LayoutType.FullPane);
      element.toggleClass('is-halfWidth', layoutType === LayoutType.SplitPane);
    }
  });

  return SearchRegion;
});


text:
layout: post
title:      "Understanding Basic JavaScript Gotchas"
date:       2018-03-22 01:30:56 -0400
permalink:  understanding_basic_javascript_gotchas
---

As the internet becomes increasingly integrated with the world outside of just the desktop, the "language of the web" becomes even more relevant. Though when I first started learning JS, I had a tough time understanding why the language had become so widely adapted. It had a bunch of weird quirks that would continuously plague the codebases that I worked with, such as conditionals returning 'true' for things that I knew should've been 'false,' and variables strangely not being defined or defined as something unexpected in my function calls. After some reading and a lot of coding, I started understanding why these things occurred, and that understanding gave way to good coding patterns, and exposed how fun/powerful JS can be.

To solidify what I've learned, here are some of the oddities that repeatedly threw me for a loop (heh) when I first started learning, why they occur, and how to avoid them:

**Weird Thing 1: Comparison Operators**

```
null > 0
//=> false
null >= 0
//=> true
null == 0
//=> false
null <= 10
//=> true
null < 0
//=> false

// Don't you dare question JS's logic!
```

The example above seems odd and nonsensical, but it's really the result of JS's engine being too nice and refusing to throw errors when comparing things that we have no business comparing.

There are two types of equality operators in JS which are used to return booleans (```true``` or ```false```) for conditional operations: the loose equality operator (==) and the strict equality operator (===). The distinction matters because the JS engine is too nice. It will attempt to perform a type conversion to make the two values comparable, if it thinks it can. This includes goofy things like changing 'null' to be a number.

```
42 == '42'
// => true
```

As you'd imagine, this can have unintended consequences. The strict equality operator never performs type conversions, and should ALWAYS be your go-to for comparisons:

```
42 === '42'
// => false
```

**Weird Thing 2: 'this' & Scope**

At first, I thought 'this' in JS was similar to 'self' in Ruby, where it points to the object where the keyword is referenced. Not necessarily! In JS, where not everything is an object, the value of 'this' is specifically determined by whether or not it's referenced *within* a method (i.e. a function that's a property of a JS object). If 'this' is referenced inside a method, then its value is the object which received the method call. If it's called outside of a method, its value is automatically the global scope. This can cause confusion in nested functions:

```
let person = {
  greet: function greeting(){
   function otherFunction(){
     return this
   }
   return otherFunction()
  }
}

person.greet()

//=> window
```

If we had returned 'this' directly within person's method greet(), we would have returned 'person.' Because person's greet() method contains and ultimately returns the 'this' value of another inner function, otherFunction(), 'this' points to the global/window scope. This is because otherFunction(), which is the function that references 'this,' is not a method (it is not a function that's directly a property of an object).

JS lets us explicitly set the value of 'this' to whatever we'd like, by using the call() or apply() methods:

```
let sally = {name: 'sally'}

function greet(customer) {
  console.log(`Hi ${customer}, my name is ${this.name}!`);
}

let newGreet = greet.call(sally);

newGreet('Bob')

//=> Hi Bob, my name is sally!
```

Similarly, to preserve the value of 'this' in callbacks, we can use bind():

```
const getUserInfo = function(callback) {
	// ajax call to fetch data asynchronously
	var numOfComments = 100;
	callback({ comments: numOfComments });
};

const User = {
	fullName: 'Jelo Yam',
	introduce: function() {
		getUserInfo(function(data) {
			console.log(`Hi, my name is ${this.fullName} and I made ${data.comments} comments`)
		});
	}
};

User.introduce();
//=> Hi, my name is undefined and I made 100 comments

//vs.

const User = {
	fullName: 'Jelo Yam',
	introduce: function() {
		getUserInfo(function(data) {
			console.log(`Hi, my name is ${this.fullName} and I made ${data.comments} comments`)
		}.bind(this));
	}
};

User.introduce();
//=> Hi, my name is Jelo Yam and I made 100 comments
```

**Weird Thing 3: Scope & Variable Declaration**

Unlike Ruby (and many many other programming languages), JS variable declarations (at least with the original 'var' keyword) are function-scoped as opposed to being block-scoped. This can be confusing and unintuitive, especially when combined with another oddity, so-called JS 'hoisting.'

**Weird Thing 4: Hoisting & Variable Declarations with var vs. const/let**

The JavaScript engine actually makes a couple of passes over code before execution. The first phase is called *The Compilation Phase,* in which it goes through the codebase from top to bottom, allocating memory to variables and functions (the engine reads and makes note of variable and function declarations) but doesn't yet execute any of the code. In other words, it's a preparation phase to make sure that the browser has everything in place for when the code actually executes. The second phase is called *The Execution Phase,* and this is when the code actually runs.

Therefore, something like this:

```
castFiraga();

function castFiraga () {
  return 'You got burned!';
}

// => "You got burned!"
```

...will work just fine even though the function invocation is written before the function declaration. This is because, during The Compilation Phase, the JS engine has already read and devoted memory to the function declaration (```function castFiraga() { ... }```). Thanks to this previous phase, during The Execution Phase, the JS engine already knows what the first line of code, the function invocation, is referring to. Because the function declaration, in JS engine chronology, takes place at the top, even though it is written in the code last, this behavior is called 'hoisting' (because it's as if the declaration is lifted, or hoisted, to the very top).

Once again, the JS engine's behaviour is implemented to try to help the programmer, however, this can be confusing because variables and functions that you had intended to refer to one thing, may haphazardly already exist and refer to other things.

This can be especially buggy in loops:

```
var array = ["chocobo", "mog", "malboro"];

for (var i = 0; i < array.length; i++) {
    var creature = array[i];
	  setTimeout(()=> { alert(creature); }, i*1000);
}

// => 'malboro', 'malboro', 'malboro'
```

As the programmer, one would expect this function to alert 'chocobo', 'mog', 'malboro' for each of the array indexes at each iteration of the loop. The reason why this isn't so, is because the JS engine hoists the 'i' and 'creature' variable declarations outside of the loop (remember, variable declarations with the 'var' keyword are function-scoped, not block-scoped). The setTimeout() callback function references these now globally-scoped variables. Because callback functions are asynchronous, the loop therefore runs to completion culminating at the third iteration and sets the 'creature' variable outside of the loop to 'malboro' before any of our alert callbacks are executed. When they are executed, they reference a variable all pointing to the same value because the loop has already run its course.

The solutions to hoisting problems are pretty simple:

1) always declare variables and functions at the top of your scopes so things are intuitive and predictable
2) always declare variables and functions with the new ES6 keywords 'let' or 'const' (standing for 'constant,' declare variables with this keyword when you expect/want its contents to remain the same).

The fix to the above code is pretty easy, thanks to ES6's 'let' and 'const' keywords:

```
const array = ["chocobo", "mog", "malboro"];

for (let i = 0; i < array.length; i++) {
    let creature = array[i];
	  setTimeout(()=> { alert(creature); }, i*1000);
}

// => 'chocobo', 'mog', 'malboro'
```

Unlike the 'var' keyword which declares things in function-scope, the 'let' keyword declares things in block-scope, as is more intuitively expected (i.e. in this case, the 'loop' scope). Therefore, in the code above, each iteration of the loop is a new 'i' assignment within the loop, as is with 'creature,' as intended.

Not only acknowledging that these oddities exist, but understanding *why* they exist, can go a long way to writing better code, and fully unlocking the potential of a programming language.


text:
@interface EXFacebook : NSObject <RCTBridgeModule>

@end


text:
/*
 * alignment.h
 *
 *  Created on: Jan 17, 2016
 *      Author: isovic
 */

#ifndef SRC_ALIGNMENT_ALIGNMENT_H_
#define SRC_ALIGNMENT_ALIGNMENT_H_

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string>
#include <sstream>
#include <vector>

#include "../sequences/single_sequence.h"
#include "../utility/utility_general.h"
#include "../program_parameters.h"
#include "../utility/utility_conversion-inl.h"
#include "../containers/path_graph_entry.h"
#include "../libs/edlib.h"
#include "cigargen.h"
#include "alignment_wrappers.h"
#include "../log_system/log_system.h"
#include "../containers/region.h"
#include "../../libs/seqan/basic.h"
#include "../../libs/seqan/align.h"
#include "../../libs/seqan/sequence.h"
#include "../../libs/seqan/stream.h"
#include "../utility/evalue.h"
#include "../index/index.h"
#include "../containers/results.h"

int AlignRegion(const SingleSequence *read, const Index *index, const ProgramParameters *parameters, const EValueParams *evalue_params, bool extend_to_end, PathGraphEntry *region_results);
int SemiglobalAlignment(AlignmentFunctionType AlignmentFunction,
                        const SingleSequence *read, const Index *index, const ProgramParameters *parameters,
                        const EValueParams *evalue_params, PathGraphEntry *region_results);
int AnchoredAlignmentNew(AlignmentFunctionType AlignmentFunctionNW, AlignmentFunctionType AlignmentFunctionSHW,
                         const SingleSequence *read, const Index *index, const ProgramParameters *parameters,
                         const EValueParams *evalue_params, PathGraphEntry *region_results, bool align_end_to_end, bool spliced_alignment);

void VerboseAlignment(const SingleSequence *read, const Index *index, const ProgramParameters *parameters, const AlignmentResults *aln);

/// Determines the start and end locations for semiglobal alignment, keeping in mind the boundaries of the reference being aligned to. Works with circular alignment as well.
//int GetAlignmentWindowFromRegion(const SingleSequence *read, const Index *index, const ProgramParameters *parameters, const PathGraphEntry *region_results,
//                                 int64_t *win_start, int64_t *win_end, int64_t *win_len);
int GetL1PosInRegion(const SingleSequence *read, const Index *index, const ProgramParameters *parameters, const PathGraphEntry *region_results,
                     int64_t *l1_start, int64_t *l1_end);

// Checks if the region is linear or circular. If it's linear, only a pointer to the beginning of the region (in the index) will be returned. Otherwise, a data array will be created containing the
// concatenated region.
// Returns 0 if the region was linear, otherwise 1. Value of 1 means that manual cleanup of ret_data is required, using free().
int GetAlignmentWindowData(const SingleSequence *read, const Index *index, const ProgramParameters *parameters, const PathGraphEntry *region_results,
                           int8_t** data, int64_t* data_length, int8_t **pos_of_win_start, int8_t **pos_of_win_end, int64_t* offset_from_ref_start, int64_t* pos_of_ref_end, bool *is_cleanup_required);

int FindCircularEnd(const std::vector<uint8_t> &alignment, int64_t pos_of_ref_end,
                    int64_t *ret_end_on_aln, int64_t *ret_end_on_read, int64_t *ret_end_on_ref,
                    int64_t *ret_start_on_aln, int64_t *ret_start_on_read, int64_t *ret_start_on_ref);

int SplitCircularAlignment(const AlignmentResults *aln, int64_t pos_of_ref_end, int64_t ref_start, int64_t ref_len, AlignmentResults *aln_l, AlignmentResults *aln_r);

int CheckAlignmentSane(std::vector<unsigned char> &alignment, const SingleSequence* read=NULL, const Index* index=NULL, int64_t reference_hit_id=-1, int64_t reference_hit_pos=-1);

#endif /* SRC_ALIGNMENT_ALIGNMENT_H_ */


text:
package ru.job4j.domain;

import javax.persistence.*;
import java.util.Objects;

/**
 * Gearbox model.
 * @author aeremeev.
 * @version 1
 * @since 01.02.2018
 */
@Entity(name = "gearbox")
public class Gearbox {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "shift")
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Gearbox gearbox = (Gearbox) o;
        return getId() == gearbox.getId() && Objects.equals(getName(), gearbox.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), getName());
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Gearbox{");
        sb.append("id=").append(id);
        sb.append(", shift='").append(name).append('\'');
        sb.append('}');
        return sb.toString();
    }
}


text:
package cl.monsoon.s1next.widget;

import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.ResponseBody;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import retrofit.Converter;

public final class ToStringConverterFactory extends Converter.Factory {

    @Override
    public Converter<ResponseBody, ?> fromResponseBody(Type type, Annotation[] annotations) {
        //noinspection EqualsBetweenInconvertibleTypes
        if (String.class.equals(type)) {
            return ResponseBody::string;
        }

        return null;
    }

    @Override
    public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) {
        //noinspection EqualsBetweenInconvertibleTypes
        if (String.class.equals(type)) {
            return new Converter<String, RequestBody>() {

                @Override
                public RequestBody convert(String value) throws IOException {
                    return RequestBody.create(MediaType.parse("text/plain"), value);
                }
            };
        }

        return null;
    }
}


text:
package xmpp

type Listener interface {
}

type MessageListener struct {
  callback func(message Message)
}

type UnknownListener struct {
  callback func(raw string)
}

type AnyListener struct {
  callback func(raw string)
}

type listenerList struct {
  listeners []Listener
}

func (list *listenerList) Subscribe(listener Listener) {
  list.listeners = append(list.listeners, listener)
}

func (list *listenerList) onAny(callback func(string)) {
  list.Subscribe(AnyListener{ callback : callback })
}

func (list *listenerList) onMessage(callback func(Message)) {
  list.Subscribe(MessageListener{ callback: callback })
}

func (list *listenerList) onUnknown(callback func(string)) {
  list.Subscribe(UnknownListener{ callback : callback })
}

func (list *listenerList) fireOnMessage(msg *Message) {
  list.eachListener(func(l Listener) {
    fireIfAny(l, msg.Raw())

    if msg_l, ok := l.(MessageListener); ok {
      msg_l.callback(*msg)
    }
  })
}

func (list *listenerList) fireOnUnknown(msg string) {
  list.eachListener(func(l Listener) {
    fireIfAny(l, msg)

    if unknown_l, ok := l.(UnknownListener); ok {
      unknown_l.callback(msg)
    }
  })
}

func fireIfAny(l Listener, msg string) {
  if any_l, ok := l.(AnyListener); ok {
    any_l.callback(msg)
  } else {
    // not any listener, so nothing to do
  }
}

func (list *listenerList) eachListener(callback func(Listener)) {
  for i := range list.listeners {
    callback(list.listeners[i])
  }
}