void testArrayPropHashes()
{
    std::string archiveName = "arrayHashTest.abc";
    {
        AO::WriteArchive w;
        ABCA::ArchiveWriterPtr a = w(archiveName, ABCA::MetaData());
        ABCA::ObjectWriterPtr archive = a->getTop();
        ABCA::ObjectWriterPtr child;

        // add a time sampling for later use
        std::vector < double > timeSamps(1,-4.0);
        ABCA::TimeSamplingType tst(3.0);
        ABCA::TimeSampling ts(tst, timeSamps);
        a->addTimeSampling(ts);

        // 2 objects without any properties whatsoever
        archive->createChild(ABCA::ObjectHeader("emptyA", ABCA::MetaData()));
        archive->createChild(ABCA::ObjectHeader("emptyB", ABCA::MetaData()));

        // 2 objects with with the same property with no samples
        child = archive->createChild(ABCA::ObjectHeader(
            "1propA", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);

        child = archive->createChild(ABCA::ObjectHeader(
            "1propB", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);

        // 2 objects with with the same property with a differnt name from above
        child = archive->createChild(ABCA::ObjectHeader(
            "1propAName", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("null",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);

        child = archive->createChild(ABCA::ObjectHeader(
            "1propBName", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("null",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);

        // 2 objects with with the same property with a differnt MetaData
        ABCA::MetaData m;
        m.set("Bleep", "bloop");
        child = archive->createChild(ABCA::ObjectHeader(
            "1propAMeta", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty", m,
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);

        child = archive->createChild(ABCA::ObjectHeader(
            "1propBMeta", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty", m,
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);

        // 2 objects with with the same property with a different POD
        child = archive->createChild(ABCA::ObjectHeader(
            "1propAPod", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kFloat32POD, 1), 0);

        child = archive->createChild(ABCA::ObjectHeader(
            "1propBPod", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kFloat32POD, 1), 0);

        // 2 objects with with the same property with a different extent
        child = archive->createChild(ABCA::ObjectHeader(
            "1propAExtent", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 2), 0);

        child = archive->createChild(ABCA::ObjectHeader(
            "1propBExtent", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 2), 0);

        // 2 objects with with the same property with a differnt time sampling
        child = archive->createChild(ABCA::ObjectHeader(
            "1propATS", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 1);

        child = archive->createChild(ABCA::ObjectHeader(
            "1propBTS", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 1);

        // 2 objects with 1 sample
        ABCA::ArrayPropertyWriterPtr awp;
        std::vector <Alembic::Util::int32_t> vali(4, 0);
        Alembic::Util::Dimensions dims(4);
        ABCA::DataType i32d(Alembic::Util::kInt32POD, 1);

        child = archive->createChild(ABCA::ObjectHeader(
            "1propA1Samp", ABCA::MetaData()));
        awp = child->getProperties()->createArrayProperty("int",
            ABCA::MetaData(), ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dims));

        child = archive->createChild(ABCA::ObjectHeader(
            "1propB1Samp", ABCA::MetaData()));
        awp = child->getProperties()->createArrayProperty("int",
            ABCA::MetaData(), ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dims));

        // 2 objects with 2 samples, no repeats
        std::vector <Alembic::Util::int32_t> valiB(4, 1);

        child = archive->createChild(ABCA::ObjectHeader(
            "1propA2Samp", ABCA::MetaData()));
        awp = child->getProperties()->createArrayProperty("int",
            ABCA::MetaData(), ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dims));
        awp->setSample(ABCA::ArraySample(&(valiB.front()), i32d, dims));

        child = archive->createChild(ABCA::ObjectHeader(
            "1propB2Samp", ABCA::MetaData()));
        awp = child->getProperties()->createArrayProperty("int",
            ABCA::MetaData(), ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dims));
        awp->setSample(ABCA::ArraySample(&(valiB.front()), i32d, dims));

        // 2 objects with 4 samples, with repeats
        child = archive->createChild(ABCA::ObjectHeader(
            "1propA4Samp", ABCA::MetaData()));
        awp = child->getProperties()->createArrayProperty("int",
            ABCA::MetaData(), ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dims));
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dims));
        awp->setSample(ABCA::ArraySample(&(valiB.front()), i32d, dims));
        awp->setSample(ABCA::ArraySample(&(valiB.front()), i32d, dims));

        child = archive->createChild(ABCA::ObjectHeader(
            "1propB4Samp", ABCA::MetaData()));
        awp = child->getProperties()->createArrayProperty("int",
            ABCA::MetaData(), ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dims));
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dims));
        awp->setSample(ABCA::ArraySample(&(valiB.front()), i32d, dims));
        awp->setSample(ABCA::ArraySample(&(valiB.front()), i32d, dims));

        // 2 objects with 1 samplem different dimensions
        Alembic::Util::Dimensions dimsB;
        dimsB.setRank(2);
        dimsB[0] = 2;
        dimsB[1] = 2;

        child = archive->createChild(ABCA::ObjectHeader(
            "1propA1Dims", ABCA::MetaData()));
        awp = child->getProperties()->createArrayProperty("int",
            ABCA::MetaData(), ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dimsB));

        child = archive->createChild(ABCA::ObjectHeader(
            "1propB1Dims", ABCA::MetaData()));
        awp = child->getProperties()->createArrayProperty("int",
            ABCA::MetaData(), ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        awp->setSample(ABCA::ArraySample(&(vali.front()), i32d, dimsB));

        // 2 objects with with the same 2 properties with no samples
        child = archive->createChild(ABCA::ObjectHeader(
            "2propA", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty", ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        child->getProperties()->createArrayProperty("null",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);

        child = archive->createChild(ABCA::ObjectHeader(
            "2propB", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        child->getProperties()->createArrayProperty("null",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);

        // 2 objects with with the same 2 properties created in opposite order
        child = archive->createChild(ABCA::ObjectHeader(
            "2propASwap", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("null",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);

        child = archive->createChild(ABCA::ObjectHeader(
            "2propBSwap", ABCA::MetaData()));
        child->getProperties()->createArrayProperty("null",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
        child->getProperties()->createArrayProperty("empty",  ABCA::MetaData(),
            ABCA::DataType(Alembic::Util::kInt32POD, 1), 0);
    }

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

        TESTING_ASSERT(archive->getNumChildren() == 26);

        // every 2 hashes should be the same
        for (size_t i = 0; i < archive->getNumChildren(); i += 2)
        {
            Alembic::Util::Digest dA, dB;
            TESTING_ASSERT(archive->getChild(i)->getPropertiesHash(dA) &&
                archive->getChild(i+1)->getPropertiesHash(dB));
            TESTING_ASSERT(dA == dB);
        }

        // make sure that every 2 child objects have different properties hashes
        for (size_t i = 0; i < archive->getNumChildren() / 2; ++i)
        {
            Alembic::Util::Digest dA;
            archive->getChild(i*2)->getPropertiesHash(dA);
            for (size_t j = i + 1; j < archive->getNumChildren() / 2; ++j)
            {
                Alembic::Util::Digest dB;
                archive->getChild(j*2)->getPropertiesHash(dB);
                TESTING_ASSERT(dA != dB);
            }
        }
    }
}
//-*****************************************************************************
void testTimeSamplingScalar()
{
    std::string archiveName = "timeSamplingScalar.abc";
    {
        A5::WriteArchive w;
        AbcA::ArchiveWriterPtr a = w(archiveName, AbcA::MetaData());
        AbcA::ObjectWriterPtr archive = a->getTop();

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

        // illegal time value
        TESTING_ASSERT_THROW(parent->createScalarProperty("uniform",
            AbcA::MetaData(), AbcA::DataType(Alembic::Util::kInt32POD, 1), 42),
            Alembic::Util::Exception);

        std::vector < double > timeSamps(1,-4.0);
        AbcA::TimeSamplingType tst(3.0);
        AbcA::TimeSampling ts(tst, timeSamps);
        uint32_t tsid = a->addTimeSampling(ts);

        AbcA::ScalarPropertyWriterPtr swp =
            parent->createScalarProperty("uniform", AbcA::MetaData(),
                AbcA::DataType(Alembic::Util::kInt32POD, 1), tsid);

        Alembic::Util::int32_t i = 0;

        swp->setSample(&i);

        i+=3;
        swp->setSample(&i);

        i+=3;
        swp->setSample(&i);

        timeSamps.clear();
        timeSamps.push_back(4.0);
        timeSamps.push_back(4.25);
        timeSamps.push_back(4.5);
        tst = AbcA::TimeSamplingType(3, 2.0);
        ts = AbcA::TimeSampling(tst, timeSamps);
        tsid = a->addTimeSampling(ts);

        AbcA::ScalarPropertyWriterPtr swp2 =
            parent->createScalarProperty("cyclic", AbcA::MetaData(),
                AbcA::DataType(Alembic::Util::kUint32POD, 1), tsid);

        Alembic::Util::uint32_t ui = 0;

        swp2->setSample(&ui);

        ui++;
        swp2->setSample(&ui);

        ui++;
        swp2->setSample(&ui);

        ui++;
        swp2->setSample(&ui);

        ui++;
        swp2->setSample(&ui);

        ui++;
        swp2->setSample(&ui);

        timeSamps.clear();
        timeSamps.push_back(-17.0);
        timeSamps.push_back(32.0);
        timeSamps.push_back(50.0);
        timeSamps.push_back(60.2);
        timeSamps.push_back(101.1);
        timeSamps.push_back(700.0);
        timeSamps.push_back(747.0);

        tst = AbcA::TimeSamplingType(AbcA::TimeSamplingType::kAcyclic);
        ts = AbcA::TimeSampling(tst, timeSamps);
        tsid = a->addTimeSampling(ts);

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

        Alembic::Util::uint16_t s = 0;
        swp3->setSample(&s);

        s++;
        swp3->setSample(&s);

        s++;

        swp3->setSample(&s);


        s++;
        swp3->setSample(&s);

        s++;
        swp3->setSample(&s);

        s++;
        swp3->setSample(&s);

        s++;
        swp3->setSample(&s);

        // Setting more than what we have acyclic samples for
        TESTING_ASSERT_THROW(swp3->setSample(&s),
            Alembic::Util::Exception);

        AbcA::ScalarPropertyWriterPtr swp4 =
            parent->createScalarProperty("identity", AbcA::MetaData(),
                AbcA::DataType(Alembic::Util::kInt16POD, 1), 0);

        Alembic::Util::int16_t ss = 35;


        swp4->setSample(&ss);

        ss = 37;
        swp4->setSample(&ss);

        ss = 1000;
        swp4->setSample(&ss);

        timeSamps.clear();
        timeSamps.push_back(0.0);
        tst = AbcA::TimeSamplingType(1.0);
        ts = AbcA::TimeSampling(tst, timeSamps);
        tsid = a->addTimeSampling(ts);

        AbcA::ScalarPropertyWriterPtr swp5 =
            parent->createScalarProperty("defaultUniform", AbcA::MetaData(),
                AbcA::DataType(Alembic::Util::kFloat32POD, 1), tsid);

        Alembic::Util::float32_t f = 0;

        swp5->setSample(&f);

        f+=1.1;
        swp5->setSample(&f);

        f+=1.1;
        swp5->setSample(&f);

        f+=1.1;
        swp5->setSample(&f);
    }

    {
        A5::ReadArchive r;
        AbcA::ArchiveReaderPtr a = r( archiveName );
        AbcA::ObjectReaderPtr archive = a->getTop();
        AbcA::CompoundPropertyReaderPtr parent = archive->getProperties();
        TESTING_ASSERT(parent->getNumProperties() == 5);

        for ( size_t i = 0; i < parent->getNumProperties(); ++i)
        {
            AbcA::BasePropertyReaderPtr bp = parent->getProperty( i );
            AbcA::ScalarPropertyReaderPtr sp = bp->asScalarPtr();
            const AbcA::TimeSamplingPtr t = sp->getTimeSampling();

            switch (sp->getDataType().getPod())
            {
                case Alembic::Util::kInt16POD:
                {
                    // identity
                    TESTING_ASSERT( t->getTimeSamplingType().isUniform() );
                }
                break;

                case Alembic::Util::kUint16POD:
                {
                    // acylic
                    TESTING_ASSERT( t->getSampleTime(0) == -17.0 );
                    TESTING_ASSERT( sp->getNumSamples() == 7);
                    TESTING_ASSERT( t->getTimeSamplingType().isAcyclic() );
                    TESTING_ASSERT( t->getSampleTime(1) == 32.0 );
                    TESTING_ASSERT( t->getSampleTime(2) == 50.0 );
                    TESTING_ASSERT( t->getSampleTime(3) == 60.2 );
                    TESTING_ASSERT( t->getSampleTime(4) == 101.1 );
                    TESTING_ASSERT( t->getSampleTime(5) == 700.0 );
                    TESTING_ASSERT( t->getSampleTime(6) == 747.0 );
                    TESTING_ASSERT_THROW( t->getSampleTime(7),
                        Alembic::Util::Exception);
                }
                break;

                case Alembic::Util::kFloat32POD:
                {
                    TESTING_ASSERT( sp->getNumSamples() == 4);
                    TESTING_ASSERT( t->getTimeSamplingType().isUniform() );
                    TESTING_ASSERT( t->getSampleTime(0) == 0.0 );
                    TESTING_ASSERT( t->getSampleTime(1) == 1.0 );
                    TESTING_ASSERT( t->getSampleTime(2) == 2.0 );
                    TESTING_ASSERT( t->getSampleTime(3) == 3.0 );
                }
                break;

                case Alembic::Util::kInt32POD:
                {
                    // uniform
                    TESTING_ASSERT( sp->getNumSamples() == 3);
                    TESTING_ASSERT( t->getTimeSamplingType().isUniform() );
                    TESTING_ASSERT( t->getSampleTime(0) == -4.0 );
                    TESTING_ASSERT( t->getSampleTime(1) == -1.0 );
                    TESTING_ASSERT( t->getSampleTime(2) == 2.0 );
                }
                break;

                case Alembic::Util::kUint32POD:
                {
                    // cyclic
                    TESTING_ASSERT( sp->getNumSamples() == 6);
                    TESTING_ASSERT( t->getTimeSamplingType().isCyclic() );
                    TESTING_ASSERT( t->getSampleTime(0) == 4.0 );
                    TESTING_ASSERT( t->getSampleTime(1) == 4.25 );
                    TESTING_ASSERT( t->getSampleTime(2) == 4.5 );
                    TESTING_ASSERT( t->getSampleTime(3) == 6.0 );
                    TESTING_ASSERT( t->getSampleTime(4) == 6.25 );
                    TESTING_ASSERT( t->getSampleTime(5) == 6.5 );
                }
                break;

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