int main()
{
    // integral
    same(false, false);
    diff(false, true);
    same('1', '1');
    diff('1', '2');
    same(static_cast<unsigned char>('1'), static_cast<unsigned char>('1'));
    diff(static_cast<unsigned char>('1'), static_cast<unsigned char>('2'));
    same(L'1', L'1');
    diff(L'1', L'2');
    same(u'1', u'1');
    diff(u'1', u'2');
    same(U'1', U'1');
    diff(U'1', U'2');
    same(static_cast<short>(1), static_cast<short>(1));
    diff(static_cast<short>(1), static_cast<short>(2));
    same(static_cast<unsigned short>(1), static_cast<unsigned short>(1));
    diff(static_cast<unsigned short>(1), static_cast<unsigned short>(2));
    same(1, 1);
    diff(1, 2);
    same(1u, 1u);
    diff(1u, 2u);
    same(1l, 1l);
    diff(1l, 2l);
    same(1ul, 1ul);
    diff(1ul, 2ul);
    same(1ll, 1ll);
    diff(1ll, 2ll);
    same(1ull, 1ull);
    diff(1ull, 2ull);

    // enum
    enum enums { enum1, enum2 };
    same(enum1, enum1);
    diff(enum1, enum2);

    // floating point
    checkFloatingPoint<float>();
    checkFloatingPoint<double>();
    checkFloatingPoint<long double>();

    // string
    same(std::string("hi"), std::string("hi"));
    diff(std::string("hi"), std::string("hj"));
    diff(std::string("hi"), std::string("hi2"));

    // bitset
    same(std::bitset<3>("101"), std::bitset<3>("101"));
    diff(std::bitset<3>("101"), std::bitset<3>("001"));
    diff(std::bitset<3>("101"), std::bitset<3>("111"));
    diff(std::bitset<3>("101"), std::bitset<3>("100"));

    // pair
    same(std::make_pair(1, '1'), std::make_pair(1, '1'));
    diff(std::make_pair(1, '1'), std::make_pair(2, '1'));
    diff(std::make_pair(1, '1'), std::make_pair(1, '2'));

    // tuple
    same(std::make_tuple(1, '1', 1.f), std::make_tuple(1, '1', 1.f));
    diff(std::make_tuple(1, '1', 1.f), std::make_tuple(2, '1', 1.f));
    diff(std::make_tuple(1, '1', 1.f), std::make_tuple(1, '2', 1.f));
    diff(std::make_tuple(1, '1', 1.f), std::make_tuple(1, '1', 2.f));

    // pointer
    int i1 = 1, i1b = 1, i2 = 2;
    int *pi1 = &i1, *pi1b = &i1b, * pi2 = &i2, * pin = nullptr;
    same(pi1, pi1);  // same addr
    same(pi1, pi1b); // diff addr, same value
    diff(pi1, pi2);  // diff addr, diff value
    same(pin, pin);  // nullptr, same
    diff(pin, pi1);  // nullptr, diff, avoid dereferenciation, first
    diff(pi1, pin);  // nullptr, diff, avoid dereferenciation, second

    std::unique_ptr<int> ui1(pi1), u2i1(pi1), ui1b(pi1b), ui2(pi2), uin(pin);
    same(ui1, ui1);
    same(ui1, u2i1); // diff object, same addr (even if it should not happen)
    same(ui1, ui1b);
    diff(ui1, ui2);
    same(uin, uin);
    diff(uin, ui1);
    diff(ui1, uin);
    ui1.release(); u2i1.release(); ui1b.release(); ui2.release(); uin.release();

    auto deleter = [](int *){};
    std::shared_ptr<int> si1(pi1, deleter), s2i1(pi1, deleter),
        si1b(pi1b, deleter), si2(pi2, deleter), sin(pin, deleter);
    same(si1, si1);
    same(si1, s2i1); // diff object, same addr (may happen since it is a shared_ptr)
    same(si1, si1b);
    diff(si1, si2);
    same(sin, sin);
    diff(sin, si1);
    diff(si1, sin);
    si1.reset(); s2i1.reset();
    si1b.reset(); si2.reset(); sin.reset();

    boost::shared_ptr<int> bsi1(pi1, deleter), bs2i1(pi1, deleter),
        bsi1b(pi1b, deleter), bsi2(pi2, deleter), bsin(pin, deleter);
    same(bsi1, bsi1);
    same(bsi1, bs2i1); // diff object, same addr (may happen since it is a shared_ptr)
    same(bsi1, bsi1b);
    diff(bsi1, bsi2);
    same(bsin, bsin);
    diff(bsin, bsi1);
    diff(bsi1, bsin);
    bsi1.reset(); bs2i1.reset();
    bsi1b.reset(); bsi2.reset(); bsin.reset();

    // C-style array
    int a123[] = {1, 2, 3}, a123b[] = {1, 2, 3}, a223[] = {2, 2, 3}, a133[] = {1, 3, 3}, a124[] = {1, 2, 4};
    same(a123, a123);
    same(a123, a123b);
    diff(a123, a223);
    diff(a123, a133);
    diff(a123, a124);
    same("hi", "hi");
    diff("hi", "hj");

    // array
    same(std::array<int, 3>{{1, 2, 3}}, std::array<int, 3>{{1, 2, 3}});
    diff(std::array<int, 3>{{1, 2, 3}}, std::array<int, 3>{{2, 2, 3}});
    diff(std::array<int, 3>{{1, 2, 3}}, std::array<int, 3>{{1, 3, 3}});
    diff(std::array<int, 3>{{1, 2, 3}}, std::array<int, 3>{{1, 2, 4}});

    // sequence
    checkSequence<std::vector<int>>();
    checkSequence<std::deque<int>>();
    checkSequence<std::forward_list<int>>();
    checkSequence<std::list<int>>();
    checkSequence<std::set<int>>();
    checkSequence<std::multiset<int>>();

    // mapping
    checkCommonMapping<std::map<int, int>>();

    // unordered mapping
    checkCommonMapping<std::unordered_map<int, int>>();

    return 0;
}
//-*****************************************************************************
void testRepeatedScalarData()
{
    std::string archiveName = "repeatScalarData.abc";

    {
        A5::WriteArchive w;
        AbcA::ArchiveWriterPtr a = w(archiveName, AbcA::MetaData());
        AbcA::ObjectWriterPtr archive = a->getTop();

        AbcA::CompoundPropertyWriterPtr parent = archive->getProperties();

        AbcA::ScalarPropertyWriterPtr swp =
            parent->createScalarProperty("int32", AbcA::MetaData(),
                AbcA::DataType(Alembic::Util::kInt32POD, 3), 0);

        std::vector <Alembic::Util::uint32_t> ui(3);
        ui[0] = 0;
        ui[1] = 1;
        ui[2] = 2;

        std::vector <Alembic::Util::uint32_t> ui2(3);
        ui2[0] = 41;
        ui2[1] = 43;
        ui2[2] = 47;

        swp->setSample(&(ui.front()));
        swp->setSample(&(ui.front()));
        swp->setSample(&(ui2.front()));
        swp->setSample(&(ui.front()));
        swp->setSample(&(ui2.front()));
        swp->setSample(&(ui2.front()));
        swp->setSample(&(ui2.front()));
        swp->setSample(&(ui.front()));
        swp->setSample(&(ui.front()));
        swp->setSample(&(ui.front()));

        AbcA::ScalarPropertyWriterPtr swp2 =
            parent->createScalarProperty("float32", AbcA::MetaData(),
                AbcA::DataType(Alembic::Util::kFloat32POD, 1), 0);

        Alembic::Util::float32_t f = 42.0;
        Alembic::Util::float32_t f2 = -3.0;

        swp2->setSample(&f);
        swp2->setSample(&f);
        swp2->setSample(&f);
        swp2->setSample(&f2);
        swp2->setSample(&f2);
        swp2->setSample(&f2);

        AbcA::ScalarPropertyWriterPtr swp3 =
            parent->createScalarProperty("uint16", AbcA::MetaData(),
                AbcA::DataType(Alembic::Util::kUint16POD, 1), 0);

        Alembic::Util::uint16_t ui16 = 17;

        swp3->setSample(&ui16);
        swp3->setSample(&ui16);
        swp3->setSample(&ui16);
        swp3->setSample(&ui16);

        AbcA::ScalarPropertyWriterPtr swp4 =
            parent->createScalarProperty("str", AbcA::MetaData(),
                AbcA::DataType(Alembic::Util::kStringPOD, 3), 0);

        std::vector < Alembic::Util::string > strVec(3);
        strVec[0] = "Please";
        strVec[1] = "";
        strVec[2] = "work";

        std::vector < Alembic::Util::string > strVec2(3);
        strVec2[0] = "Whats";
        strVec2[1] = "going";
        strVec2[2] = "on?";

        swp4->setSample(&(strVec.front()));
        swp4->setSample(&(strVec.front()));
        swp4->setSample(&(strVec2.front()));
        swp4->setSample(&(strVec2.front()));
    }

    {
        A5::ReadArchive r;
        AbcA::ArchiveReaderPtr a = r( archiveName );
        AbcA::ObjectReaderPtr archive = a->getTop();

        AbcA::CompoundPropertyReaderPtr parent = archive->getProperties();
        TESTING_ASSERT(parent->getNumProperties() == 4);
        for (size_t i = 0; i < parent->getNumProperties(); ++i)
        {
            AbcA::BasePropertyReaderPtr bp = parent->getProperty( i );
            AbcA::ScalarPropertyReaderPtr sp = bp->asScalarPtr();
            switch (sp->getDataType().getPod())
            {
                case Alembic::Util::kUint16POD:
                {
                    TESTING_ASSERT( sp->getNumSamples() == 4 );

                    const AbcA::TimeSamplingPtr t = sp->getTimeSampling();
                    TESTING_ASSERT( sp->isConstant() );

                    Alembic::Util::uint16_t us;

                    for ( size_t i = 0; i < sp->getNumSamples(); ++i )
                    {
                        us = 0;
                        sp->getSample( 0, &us);
                        TESTING_ASSERT(us == 17);
                    }
                }
                break;

                case Alembic::Util::kFloat32POD:
                {
                    TESTING_ASSERT( sp->getNumSamples() == 6 );
                    TESTING_ASSERT( sp->getDataType().getExtent() == 1);
                    TESTING_ASSERT( !sp->isConstant() );

                    Alembic::Util::float32_t f = 0;

                    // make sure we can't get a non-existant sample
                    TESTING_ASSERT_THROW(sp->getSample( 100, &f ),
                        Alembic::Util::Exception);

                    sp->getSample( 5, &f );
                    TESTING_ASSERT(f == -3.0);

                    sp->getSample( 1, &f );
                    TESTING_ASSERT(f == 42.0);

                    sp->getSample( 4, &f );
                    TESTING_ASSERT(f == -3.0);

                    sp->getSample( 0, &f );
                    TESTING_ASSERT(f == 42.0);

                    sp->getSample( 3, &f );
                    TESTING_ASSERT(f == -3.0);

                    sp->getSample( 2, &f );
                    TESTING_ASSERT(f == 42.0);
                }
                break;

                case Alembic::Util::kInt32POD:
                {
                    TESTING_ASSERT( sp->getNumSamples() == 10 );
                    TESTING_ASSERT( sp->getDataType().getExtent() == 3);
                    TESTING_ASSERT( !sp->isConstant() );

                    std::vector< Alembic::Util::uint32_t > ui(3);

                    // lets explicitly test each sample
                    sp->getSample( 0, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 0 && ui[1] == 1 && ui[2] == 2);

                    sp->getSample( 1, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 0 && ui[1] == 1 && ui[2] == 2);

                    sp->getSample( 2, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 41 && ui[1] == 43 && ui[2] == 47);

                    sp->getSample( 3, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 0 && ui[1] == 1 && ui[2] == 2);

                    sp->getSample( 4, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 41 && ui[1] == 43 && ui[2] == 47);

                    sp->getSample( 5, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 41 && ui[1] == 43 && ui[2] == 47);

                    sp->getSample( 6, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 41 && ui[1] == 43 && ui[2] == 47);

                    sp->getSample( 7, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 0 && ui[1] == 1 && ui[2] == 2);

                    sp->getSample( 8, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 0 && ui[1] == 1 && ui[2] == 2);

                    sp->getSample( 9, &(ui.front()) );
                    TESTING_ASSERT(ui[0] == 0 && ui[1] == 1 && ui[2] == 2);

                }
                break;

                case Alembic::Util::kStringPOD:
                {
                    TESTING_ASSERT( sp->getNumSamples() == 4 );
                    TESTING_ASSERT( sp->getDataType().getExtent() == 3);
                    TESTING_ASSERT( !sp->isConstant() );

                    std::vector< Alembic::Util::string > val(3);
                    sp->getSample(0, &(val.front()));
                    TESTING_ASSERT( val[0] == "Please");
                    TESTING_ASSERT( val[1] == "");
                    TESTING_ASSERT( val[2] == "work");

                    sp->getSample(1, &(val.front()));
                    TESTING_ASSERT( val[0] == "Please");
                    TESTING_ASSERT( val[1] == "");
                    TESTING_ASSERT( val[2] == "work");

                    sp->getSample(2, &(val.front()));
                    TESTING_ASSERT( val[0] == "Whats");
                    TESTING_ASSERT( val[1] == "going");
                    TESTING_ASSERT( val[2] == "on?");

                    sp->getSample(3, &(val.front()));
                    TESTING_ASSERT( val[0] == "Whats");
                    TESTING_ASSERT( val[1] == "going");
                    TESTING_ASSERT( val[2] == "on?");
                }
                break;

                default:
                    TESTING_ASSERT(false);
                break;
            }
        } // for
    }
}