int main()
{
    int i;
    int P[100], U[4];
    const char *N[3] = { "red", "green", "blue" };
    const char *FileNumbers[] = {"1","2","3"};

    // Test a somewhat complex expression 
    Namescheme *ns = new Namescheme("@foo_%+03d@3-((n % 3)*(4+1)+1/2)+1");
    if (strcmp(ns->GetName(25), "foo_+01") != 0)
        return 1;
    delete ns;

    // Test ?:: operator
    ns = new Namescheme("@foo_%d@(n-5)?14:77:");
    if (strcmp(ns->GetName(6), "foo_14") != 0)
        return 1;
    delete ns;

    // Example of AMR-like naming convention where we have the following
    // assignment of pathces to levels starting with the patches on
    // level 0 and ending with the patches on level 3...
    //
    // level: 0 0 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3
    //           |             |                                 |
    //        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 
    // patch: 0 1 0 1 2 3 4 5 6 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 0 1 2 3 4
    //           |             |                                 |
    //        0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3
    //     n: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
    //          A   B         C D                     E         F G       H
    //
    //  2 patches at level 0
    //  7 patches at level 1
    // 17 patches at level 2
    //  5 patches at level 3
    ns = new Namescheme("@level%d,patch%d"
        "@(n/2)?((n/9)?((n/26)?3:2:):1:):0:"        // level part (1rst %d) 
        "@(n/2)?((n/9)?((n/26)?n-26:n-9:):n-2:):n:" // patch part (2nd %d)
    );
    if (strcmp(ns->GetName( 1), "level0,patch1")  != 0) return 1; // A
    if (strcmp(ns->GetName( 3), "level1,patch1")  != 0) return 1; // B
    if (strcmp(ns->GetName( 8), "level1,patch6")  != 0) return 1; // C
    if (strcmp(ns->GetName( 9), "level2,patch0")  != 0) return 1; // D
    if (strcmp(ns->GetName(20), "level2,patch11") != 0) return 1; // E
    if (strcmp(ns->GetName(25), "level2,patch16") != 0) return 1; // F
    if (strcmp(ns->GetName(26), "level3,patch0")  != 0) return 1; // G
    if (strcmp(ns->GetName(30), "level3,patch4")  != 0) return 1; // H
    delete ns;

    // Test multiple conversion specifiers
    ns = new Namescheme("|foo_%03dx%03d|n/5|n%5");
    if (strcmp(ns->GetName(17), "foo_003x002") != 0)
       return 1;
    if (strcmp(ns->GetName(20), "foo_004x000") != 0)
       return 1;
    if (strcmp(ns->GetName(3), "foo_000x003") != 0)
       return 1;
    delete ns;

    // Test embedded string value results
    ns = new Namescheme("#foo_%s#(n-5)?'master':'slave':");
    if (strcmp(ns->GetName(6), "foo_master") != 0)
        return 1;
    delete ns;

    // Test array-based references in a name scheme
    for (i = 0; i < 100; i++)
        P[i] = i*5;
    for (i = 0; i < 4; i++)
        U[i] = i*i;
    ns = new Namescheme("@foo_%03dx%03d@#P[n]@#U[n%4]", P, U);
    if (strcmp(ns->GetName(17), "foo_085x001") != 0)
        return 1;
    if (strcmp(ns->GetName(18), "foo_090x004") != 0)
        return 1;
    if (strcmp(ns->GetName(19), "foo_095x009") != 0)
        return 1;
    if (strcmp(ns->GetName(20), "foo_100x000") != 0)
        return 1;
    if (strcmp(ns->GetName(21), "foo_105x001") != 0)
        return 1;
    delete ns;

    // Test array-based references to char* valued array
    ns = new Namescheme("Hfoo_%sH$N[n%3]", N);
    if (strcmp(ns->GetName(17), "foo_blue") != 0) return 1;
    if (strcmp(ns->GetName(6), "foo_red") != 0) return 1;
    delete ns;

    // Test McCandless' example
    ns = new Namescheme("@%s%s@(n/4)?'myfilename.':'':@(n/4)?$/arr_dir/FileNumbers[n/4-1]:'':",FileNumbers);
    if (strcmp(ns->GetName(0), "") != 0) return 1;
    if (strcmp(ns->GetName(1), "") != 0) return 1;
    if (strcmp(ns->GetName(4), "myfilename.1") != 0) return 1;
    if (strcmp(ns->GetName(15), "myfilename.3") != 0) return 1;
    delete ns;

    return 0;
}
const char *NameschemeAttributes::GetName(int n) const
{
    // First, determine if this is FIRST call to GetName on this object
    map<const void*, bool>::const_iterator cit4 = getNameCalledMap.find(this);
    if (cit4 == getNameCalledMap.end())
    {
        // This is first call. Indicate that in the map.
        getNameCalledMap[this] = true;
    }

    // First, see if we have ALL explicit names defined and, if so,
    // ensure 'n' is in correct range for it.
    if ((size_t)n < allExplicitNames.size())
        return allExplicitNames[n].c_str();
        
    // Next, see if we have an explicit names map for this object.
    if (explicitIds.size())
    {
        // If there is an explicit name for this entry, return it.
        for (size_t i = 0; i < explicitIds.size(); i++)
        {
            if (explicitIds[i] == n)
                return explicitNames[i].c_str();
        }
    }

    // If we get here, we didn't have an explicit name, so
    // use our Namescheme class to compute the name.
    static char tmpname[128];
    if (namescheme == "")
    {
        SNPRINTF(tmpname, sizeof(tmpname), "unknown_name_%d", n);
        return tmpname;
    }

    // First, lets see if we've already got one constructed for this object.
    Namescheme *ns = 0;
    map<const void*, Namescheme*>::const_iterator cit3 = nameschemesMap.find(this);
    if (cit3 != nameschemesMap.end())
    {
        ns = cit3->second;
    }
    else
    {
        // We didn't find a Namescheme object. That means we've yet to create
        // it for this object. Create it now.

        // We could make this switch larger to accomodate a larger number
        // of external array references. But, for now, we're setting a 
        // limit at 4. If we want to increase it, we need to make sure the
        // cooresponding Namescheme class can support it.
        switch(externalArrayNames.size())
        {
            case 0: ns = new Namescheme(namescheme.c_str()); break;
            case 1: ns = new Namescheme(namescheme.c_str(), EA(0)); break;
            case 2: ns = new Namescheme(namescheme.c_str(), EA(0), EA(1)); break;
            case 3: ns = new Namescheme(namescheme.c_str(), EA(0), EA(1), EA(2)); break;
            case 4: ns = new Namescheme(namescheme.c_str(), EA(0), EA(1), EA(2), EA(3)); break;
            default: { EXCEPTION0(StateObjectException); }
        }

        // Store the Namescheme pointer in the map for this object so
        // we can find it again when we need to.
        nameschemesMap[this] = ns;
    }

    // Compute the name from the Namescheme
    const char *retval = ns ? ns->GetName(n) : 0;
    if (retval && retval[0] != '\0') return retval;

    // Ok, we can't compute a name. Make one up.
    SNPRINTF(tmpname, sizeof(tmpname), "unknown_name_%d", n);
    return tmpname;
}