00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "fennel/common/CommonPreamble.h"
00023 #include "fennel/common/FemEnums.h"
00024 #include "fennel/test/ExecStreamUnitTestBase.h"
00025 #include "fennel/lucidera/colstore/LcsClusterAppendExecStream.h"
00026 #include "fennel/sorter/ExternalSortExecStream.h"
00027 #include "fennel/lucidera/bitmap/LbmGeneratorExecStream.h"
00028 #include "fennel/lucidera/bitmap/LbmSplicerExecStream.h"
00029 #include "fennel/lucidera/bitmap/LbmEntryDump.h"
00030 #include "fennel/btree/BTreeBuilder.h"
00031 #include "fennel/ftrs/BTreeInsertExecStream.h"
00032 #include "fennel/ftrs/BTreeSearchExecStream.h"
00033 #include "fennel/ftrs/BTreeExecStream.h"
00034 #include "fennel/tuple/StandardTypeDescriptor.h"
00035 #include "fennel/tuple/TupleDescriptor.h"
00036 #include "fennel/exec/MockProducerExecStream.h"
00037 #include "fennel/exec/ExecStreamEmbryo.h"
00038 #include "fennel/exec/SplitterExecStream.h"
00039 #include "fennel/exec/BarrierExecStream.h"
00040 #include "fennel/exec/ExecStreamGraph.h"
00041 #include "fennel/cache/Cache.h"
00042 #include "fennel/common/TraceSource.h"
00043 #include <stdarg.h>
00044
00045 #include <boost/test/test_tools.hpp>
00046
00047 using namespace fennel;
00048
00052 class LbmLoadBitmapTest : public ExecStreamUnitTestBase
00053 {
00054 protected:
00055 StandardTypeDescriptorFactory stdTypeFactory;
00056 TupleAttributeDescriptor attrDesc_int64;
00057
00061 vector<boost::shared_ptr<BTreeDescriptor> > bTreeClusters;
00062
00067 vector<PageId> savedBTreeClusterRootIds;
00068
00072 vector<boost::shared_ptr<BTreeDescriptor> > bTreeBitmaps;
00073
00078 vector<PageId> savedBTreeBitmapRootIds;
00079
00083 vector<boost::shared_ptr<LbmEntryDump> > entryDumps;
00084
00088 void initBTreeExecStreamParam(
00089 BTreeExecStreamParams ¶m, shared_ptr<BTreeDescriptor> pBTreeDesc);
00090
00094 void initBTreeParam(
00095 BTreeParams ¶m, shared_ptr<BTreeDescriptor> pBTreeDesc);
00096
00100 void initClusterScanDef(
00101 LbmGeneratorExecStreamParams &generatorParams,
00102 struct LcsClusterScanDef &clusterScanDef,
00103 uint bTreeIndex,
00104 DynamicParamId paramId);
00105
00109 void initBTreeBitmapDesc(
00110 TupleDescriptor ¶m, TupleProjection &keyProj, uint nKeys);
00111
00115 void initBTreeTupleDesc(TupleDescriptor &tupleDesc, uint nKeys);
00116
00117 void testLoad(
00118 uint nRows, uint nClusters, std::vector<int> &repeatSeqValues,
00119 bool newRoot, bool dumpEntries, string testName,
00120 bool dynamicRootPageId);
00121
00122 public:
00123 explicit LbmLoadBitmapTest()
00124 {
00125 FENNEL_UNIT_TEST_CASE(LbmLoadBitmapTest, testLoad50);
00126 FENNEL_UNIT_TEST_CASE(LbmLoadBitmapTest, testLoad5000);
00127 FENNEL_UNIT_TEST_CASE(LbmLoadBitmapTest, testLoad10000);
00128 FENNEL_UNIT_TEST_CASE(LbmLoadBitmapTest, testAppend);
00129 FENNEL_UNIT_TEST_CASE(LbmLoadBitmapTest, testLoadDynamicRoot50);
00130 }
00131
00132 void testCaseSetUp();
00133 void testCaseTearDown();
00134
00135 void testLoadSmall(bool dynamicRootPageId);
00136
00137 void testLoad50();
00138 void testLoad5000();
00139 void testLoad10000();
00140 void testAppend();
00141 void testLoadDynamicRoot50();
00142 };
00143
00144 void LbmLoadBitmapTest::testLoad50()
00145 {
00146 testLoadSmall(false);
00147 }
00148
00149 void LbmLoadBitmapTest::testLoadDynamicRoot50()
00150 {
00151 testLoadSmall(true);
00152 }
00153
00154 void LbmLoadBitmapTest::testLoadSmall(bool dynamicRootPageId)
00155 {
00156
00157 uint nRows = 50;
00158 uint nClusters = 4;
00159 std::vector<int> repeatSeqValues;
00160
00161
00162
00163 repeatSeqValues.push_back(nRows);
00164 repeatSeqValues.push_back(5);
00165 repeatSeqValues.push_back(9);
00166 repeatSeqValues.push_back(19);
00167 testLoad(
00168 nRows, nClusters, repeatSeqValues, true, true, "testLoad50",
00169 dynamicRootPageId);
00170 }
00171
00172 void LbmLoadBitmapTest::testLoad5000()
00173 {
00174 uint nRows = 5000;
00175 uint nClusters = 2;
00176 std::vector<int> repeatSeqValues;
00177
00178
00179 repeatSeqValues.push_back(nRows);
00180 repeatSeqValues.push_back(200);
00181 testLoad(
00182 nRows, nClusters, repeatSeqValues, true, true, "testLoad5000", false);
00183 }
00184
00185 void LbmLoadBitmapTest::testLoad10000()
00186 {
00187
00188 uint nRows = 10000;
00189 uint nClusters = 4;
00190 std::vector<int> repeatSeqValues;
00191
00192 repeatSeqValues.push_back(nRows);
00193 repeatSeqValues.push_back(5);
00194 repeatSeqValues.push_back(9);
00195 repeatSeqValues.push_back(19);
00196 testLoad(
00197 nRows, nClusters, repeatSeqValues, true, true, "testLoad10000", false);
00198 }
00199
00200 void LbmLoadBitmapTest::testAppend()
00201 {
00202
00203 uint nRows = 60;
00204 uint nClusters = 4;
00205 std::vector<int> repeatSeqValues1;
00206 std::vector<int> repeatSeqValues2;
00207
00208
00209
00210
00211 repeatSeqValues1.push_back(nRows);
00212 repeatSeqValues2.push_back(nRows);
00213
00214
00215 repeatSeqValues1.push_back(23);
00216 repeatSeqValues2.push_back(31);
00217
00218
00219 repeatSeqValues1.push_back(1);
00220 repeatSeqValues2.push_back(2);
00221
00222
00223 repeatSeqValues1.push_back(7);
00224 repeatSeqValues2.push_back(29);
00225
00226
00227 testLoad(
00228 nRows, nClusters, repeatSeqValues1, true, true, "testAppendNewRoot",
00229 false);
00230
00231
00232 resetExecStreamTest();
00233 testLoad(
00234 nRows, nClusters, repeatSeqValues2, false, true, "testAppendOldRoot",
00235 false);
00236 }
00237
00249 void LbmLoadBitmapTest::testLoad(
00250 uint nRows, uint nClusters, std::vector<int> &repeatSeqValues, bool newRoot,
00251 bool dumpEntries, string testName, bool dynamicRootPageId)
00252 {
00253
00254 for (uint i = 0; i < bTreeClusters.size(); i++) {
00255 bTreeClusters[i]->segmentAccessor.reset();
00256 }
00257 for (uint i = 0; i < bTreeBitmaps.size(); i++) {
00258 bTreeBitmaps[i]->segmentAccessor.reset();
00259 }
00260 bTreeClusters.clear();
00261 bTreeBitmaps.clear();
00262 entryDumps.clear();
00263
00264
00265
00266 MockProducerExecStreamParams mockParams;
00267 for (uint i = 0; i < nClusters; i++) {
00268 mockParams.outputTupleDesc.push_back(attrDesc_int64);
00269 }
00270 mockParams.nRows = nRows;
00271
00272 vector<boost::shared_ptr<ColumnGenerator<int64_t> > > columnGenerators;
00273 SharedInt64ColumnGenerator col;
00274 assert(repeatSeqValues.size() == nClusters);
00275 for (uint i = 0; i < repeatSeqValues.size(); i++) {
00276 col =
00277 SharedInt64ColumnGenerator(
00278 new RepeatingSeqColumnGenerator(repeatSeqValues[i]));
00279 columnGenerators.push_back(col);
00280 }
00281 mockParams.pGenerator.reset(
00282 new CompositeExecStreamGenerator(columnGenerators));
00283
00284 ExecStreamEmbryo mockStreamEmbryo;
00285 mockStreamEmbryo.init(new MockProducerExecStream(), mockParams);
00286 mockStreamEmbryo.getStream()->setName("MockProducerExecStream");
00287
00288
00289
00290 SplitterExecStreamParams splitterParams;
00291 ExecStreamEmbryo splitterStreamEmbryo;
00292 splitterStreamEmbryo.init(new SplitterExecStream(), splitterParams);
00293 splitterStreamEmbryo.getStream()->setName("ClusterSplitterExecStream");
00294
00295
00296
00297 vector<ExecStreamEmbryo> lcsAppendEmbryos;
00298 for (uint i = 0; i < nClusters; i++) {
00299 LcsClusterAppendExecStreamParams lcsAppendParams;
00300 boost::shared_ptr<BTreeDescriptor> pBTreeDesc =
00301 boost::shared_ptr<BTreeDescriptor> (new BTreeDescriptor());
00302 bTreeClusters.push_back(pBTreeDesc);
00303
00304
00305
00306 (lcsAppendParams.tupleDesc).push_back(attrDesc_int64);
00307 (lcsAppendParams.tupleDesc).push_back(attrDesc_int64);
00308
00309
00310 (lcsAppendParams.keyProj).push_back(0);
00311
00312 initBTreeExecStreamParam(lcsAppendParams, pBTreeDesc);
00313
00314
00315 lcsAppendParams.outputTupleDesc.push_back(attrDesc_int64);
00316 lcsAppendParams.outputTupleDesc.push_back(attrDesc_int64);
00317
00318 lcsAppendParams.inputProj.push_back(i);
00319
00320
00321
00322 if (newRoot) {
00323 BTreeBuilder builder(*pBTreeDesc, pRandomSegment);
00324 builder.createEmptyRoot();
00325 savedBTreeClusterRootIds.push_back(builder.getRootPageId());
00326 }
00327 lcsAppendParams.rootPageId = pBTreeDesc->rootPageId =
00328 savedBTreeClusterRootIds[i];
00329
00330
00331
00332 ExecStreamEmbryo lcsAppendStreamEmbryo;
00333 lcsAppendStreamEmbryo.init(
00334 new LcsClusterAppendExecStream(), lcsAppendParams);
00335 std::ostringstream oss;
00336 oss << "LcsClusterAppendExecStream" << "#" << i;
00337 lcsAppendStreamEmbryo.getStream()->setName(oss.str());
00338 lcsAppendEmbryos.push_back(lcsAppendStreamEmbryo);
00339 }
00340
00341
00342
00343 BarrierExecStreamParams barrierParams;
00344 barrierParams.outputTupleDesc.push_back(attrDesc_int64);
00345 barrierParams.outputTupleDesc.push_back(attrDesc_int64);
00346 barrierParams.returnMode = BARRIER_RET_ANY_INPUT;
00347
00348 ExecStreamEmbryo clusterBarrierStreamEmbryo;
00349 clusterBarrierStreamEmbryo.init(new BarrierExecStream(), barrierParams);
00350 clusterBarrierStreamEmbryo.getStream()->setName("ClusterBarrierExecStream");
00351
00352
00353 prepareDAG(
00354 mockStreamEmbryo, splitterStreamEmbryo, lcsAppendEmbryos,
00355 clusterBarrierStreamEmbryo, false);
00356
00357
00358
00359 splitterStreamEmbryo.init(
00360 new SplitterExecStream(), splitterParams);
00361 splitterStreamEmbryo.getStream()->setName("BitmapSplitterExecStream");
00362
00363
00364
00365
00366 std::vector<std::vector<ExecStreamEmbryo> > createBitmapStreamList;
00367 for (uint i = 0; i < nClusters + 1; i++) {
00368 if (i == 1 && nClusters == 1) {
00369
00370
00371
00372
00373 break;
00374 }
00375
00376 std::vector<ExecStreamEmbryo> createBitmapStream;
00377
00378
00379
00380 LbmGeneratorExecStreamParams generatorParams;
00381 struct LcsClusterScanDef clusterScanDef;
00382 clusterScanDef.clusterTupleDesc.push_back(attrDesc_int64);
00383
00384
00385
00386 if (i < nClusters) {
00387 DynamicParamId paramId =
00388 (dynamicRootPageId) ?
00389 DynamicParamId(nClusters + i + 2) : DynamicParamId(0);
00390 initClusterScanDef(generatorParams, clusterScanDef, i, paramId);
00391 } else {
00392 for (uint j = 0; j < nClusters; j++) {
00393 DynamicParamId paramId =
00394 (dynamicRootPageId) ? DynamicParamId(nClusters + 2 + j) :
00395 DynamicParamId(0);
00396 initClusterScanDef(generatorParams, clusterScanDef, j, paramId);
00397 }
00398 }
00399
00400 TupleProjection proj;
00401 if (i < nClusters) {
00402 proj.push_back(0);
00403 } else {
00404 for (uint j = 0; j < nClusters; j++) {
00405 proj.push_back(j);
00406 }
00407 }
00408 generatorParams.outputProj = proj;
00409 generatorParams.insertRowCountParamId = DynamicParamId(i + 1);
00410 generatorParams.createIndex = false;
00411
00412 boost::shared_ptr<BTreeDescriptor> pBTreeDesc =
00413 boost::shared_ptr<BTreeDescriptor> (new BTreeDescriptor());
00414 bTreeBitmaps.push_back(pBTreeDesc);
00415
00416
00417
00418
00419 if (dumpEntries) {
00420 ostringstream traceName;
00421 traceName << testName << " Index " << i;
00422 boost::shared_ptr<LbmEntryDump> pEntryDump =
00423 boost::shared_ptr<LbmEntryDump>(
00424 new LbmEntryDump(
00425 TRACE_INFO,
00426 shared_from_this(),
00427 traceName.str()));
00428 entryDumps.push_back(pEntryDump);
00429 }
00430
00431
00432
00433 uint nKeys;
00434 if (i < nClusters) {
00435 nKeys = 2;
00436 } else {
00437 nKeys = nClusters + 1;
00438 }
00439 initBTreeTupleDesc(generatorParams.outputTupleDesc, nKeys);
00440
00441 initBTreeBitmapDesc(
00442 generatorParams.tupleDesc, generatorParams.keyProj, nKeys);
00443 initBTreeExecStreamParam(generatorParams, pBTreeDesc);
00444
00445
00446
00447 if (newRoot) {
00448 BTreeBuilder builder(*pBTreeDesc, pRandomSegment);
00449 builder.createEmptyRoot();
00450 savedBTreeBitmapRootIds.push_back(builder.getRootPageId());
00451 }
00452 generatorParams.rootPageId = pBTreeDesc->rootPageId =
00453 savedBTreeBitmapRootIds[i];
00454 if (dynamicRootPageId && i < nClusters) {
00455 SharedDynamicParamManager pDynamicParamManager =
00456 pGraph->getDynamicParamManager();
00457 DynamicParamId paramId = DynamicParamId(nClusters + i + 2);
00458 pDynamicParamManager->createParam(paramId, attrDesc_int64);
00459 TupleDatum pageIdDatum;
00460 PageId rootPageId = savedBTreeBitmapRootIds[i];
00461 pageIdDatum.pData = (PConstBuffer) &rootPageId;
00462 pageIdDatum.cbData = sizeof(PageId);
00463 pDynamicParamManager->writeParam(paramId, pageIdDatum);
00464 }
00465
00466 ExecStreamEmbryo generatorStreamEmbryo;
00467 generatorStreamEmbryo.init(
00468 new LbmGeneratorExecStream(), generatorParams);
00469 std::ostringstream oss;
00470 oss << "LbmGeneratorExecStream" << "#" << i;
00471 generatorStreamEmbryo.getStream()->setName(oss.str());
00472 createBitmapStream.push_back(generatorStreamEmbryo);
00473
00474
00475
00476 ExternalSortExecStreamParams sortParams;
00477 initBTreeBitmapDesc(
00478 sortParams.outputTupleDesc, sortParams.keyProj, nKeys);
00479 sortParams.distinctness = DUP_ALLOW;
00480 sortParams.pTempSegment = pRandomSegment;
00481 sortParams.pCacheAccessor = pCache;
00482 sortParams.scratchAccessor =
00483 pSegmentFactory->newScratchSegment(pCache, 10);
00484 sortParams.storeFinalRun = false;
00485 sortParams.estimatedNumRows = MAXU;
00486 sortParams.earlyClose = false;
00487
00488 ExecStreamEmbryo sortStreamEmbryo;
00489 sortStreamEmbryo.init(
00490 ExternalSortExecStream::newExternalSortExecStream(), sortParams);
00491 sortStreamEmbryo.getStream()->setName("ExternalSortExecStream");
00492 std::ostringstream oss2;
00493 oss2 << "ExternalSortExecStream" << "#" << i;
00494 sortStreamEmbryo.getStream()->setName(oss2.str());
00495 createBitmapStream.push_back(sortStreamEmbryo);
00496
00497
00498
00499 LbmSplicerExecStreamParams splicerParams;
00500 splicerParams.createNewIndex = false;
00501 splicerParams.scratchAccessor =
00502 pSegmentFactory->newScratchSegment(pCache, 15);
00503 splicerParams.pCacheAccessor = pCache;
00504 BTreeParams bTreeParams;
00505 initBTreeBitmapDesc(
00506 bTreeParams.tupleDesc, bTreeParams.keyProj, nKeys);
00507 initBTreeParam(bTreeParams, pBTreeDesc);
00508 bTreeParams.rootPageId = pBTreeDesc->rootPageId;
00509 splicerParams.bTreeParams.push_back(bTreeParams);
00510 splicerParams.insertRowCountParamId = DynamicParamId(i + 1);
00511 splicerParams.writeRowCountParamId = DynamicParamId(0);
00512 splicerParams.outputTupleDesc.push_back(attrDesc_int64);
00513
00514 ExecStreamEmbryo splicerStreamEmbryo;
00515 splicerStreamEmbryo.init(new LbmSplicerExecStream(), splicerParams);
00516 std::ostringstream oss3;
00517 oss3 << "LbmSplicerExecStream" << "#" << i;
00518 splicerStreamEmbryo.getStream()->setName(oss3.str());
00519 createBitmapStream.push_back(splicerStreamEmbryo);
00520
00521
00522
00523 createBitmapStreamList.push_back(createBitmapStream);
00524 }
00525
00526
00527
00528 barrierParams.outputTupleDesc.clear();
00529 barrierParams.outputTupleDesc.push_back(attrDesc_int64);
00530
00531 ExecStreamEmbryo barrierStreamEmbryo;
00532 barrierStreamEmbryo.init(
00533 new BarrierExecStream(), barrierParams);
00534 barrierStreamEmbryo.getStream()->setName("BitmapBarrierExecStream");
00535
00536
00537
00538 SharedExecStream pOutputStream = prepareDAG(
00539 clusterBarrierStreamEmbryo, splitterStreamEmbryo,
00540 createBitmapStreamList, barrierStreamEmbryo, true, false);
00541
00542
00543 RampExecStreamGenerator expectedResultGenerator(mockParams.nRows);
00544
00545 verifyOutput(*pOutputStream, 1, expectedResultGenerator);
00546
00547 if (dumpEntries) {
00548 for (uint i = 0; i < entryDumps.size(); i++) {
00549 entryDumps[i]->dump(*(bTreeBitmaps[i].get()), true);
00550 }
00551 }
00552 }
00553
00554 void LbmLoadBitmapTest::initBTreeExecStreamParam(
00555 BTreeExecStreamParams ¶m, shared_ptr<BTreeDescriptor> pBTreeDesc)
00556 {
00557 param.scratchAccessor = pSegmentFactory->newScratchSegment(pCache, 15);
00558 param.pCacheAccessor = pCache;
00559 initBTreeParam(param, pBTreeDesc);
00560 }
00561
00562 void LbmLoadBitmapTest::initBTreeParam(
00563 BTreeParams ¶m, shared_ptr<BTreeDescriptor> pBTreeDesc)
00564 {
00565 param.pSegment = pRandomSegment;
00566 param.pRootMap = 0;
00567 param.rootPageIdParamId = DynamicParamId(0);
00568
00569 pBTreeDesc->segmentAccessor.pSegment = param.pSegment;
00570 pBTreeDesc->segmentAccessor.pCacheAccessor = pCache;
00571 pBTreeDesc->tupleDescriptor = param.tupleDesc;
00572 pBTreeDesc->keyProjection = param.keyProj;
00573 param.pageOwnerId = pBTreeDesc->pageOwnerId;
00574 param.segmentId = pBTreeDesc->segmentId;
00575 }
00576
00577 void LbmLoadBitmapTest::initClusterScanDef(
00578 LbmGeneratorExecStreamParams &generatorParams,
00579 struct LcsClusterScanDef &clusterScanDef,
00580 uint bTreeIndex,
00581 DynamicParamId paramId)
00582 {
00583 clusterScanDef.pSegment =
00584 bTreeClusters[bTreeIndex]->segmentAccessor.pSegment;
00585 clusterScanDef.pCacheAccessor =
00586 bTreeClusters[bTreeIndex]->segmentAccessor.pCacheAccessor;
00587 clusterScanDef.tupleDesc = bTreeClusters[bTreeIndex]->tupleDescriptor;
00588 clusterScanDef.keyProj = bTreeClusters[bTreeIndex]->keyProjection;
00589 clusterScanDef.rootPageIdParamId = paramId;
00590 clusterScanDef.rootPageId =
00591 (opaqueToInt(paramId) > 0) ?
00592 NULL_PAGE_ID : bTreeClusters[bTreeIndex]->rootPageId;
00593 clusterScanDef.pageOwnerId = bTreeClusters[bTreeIndex]->pageOwnerId;
00594 clusterScanDef.segmentId = bTreeClusters[bTreeIndex]->segmentId;
00595 clusterScanDef.pRootMap = 0;
00596 generatorParams.lcsClusterScanDefs.push_back(clusterScanDef);
00597 }
00598
00599 void LbmLoadBitmapTest::initBTreeBitmapDesc(
00600 TupleDescriptor &tupleDesc, TupleProjection &keyProj, uint nKeys)
00601 {
00602 initBTreeTupleDesc(tupleDesc, nKeys);
00603
00604
00605 for (uint j = 0; j < nKeys; j++) {
00606 keyProj.push_back(j);
00607 }
00608 }
00609
00610 void LbmLoadBitmapTest::initBTreeTupleDesc(
00611 TupleDescriptor &tupleDesc, uint nKeys)
00612 {
00613 for (uint i = 0; i < nKeys; i++) {
00614 tupleDesc.push_back(attrDesc_int64);
00615 }
00616 uint varColSize;
00617
00618
00619 varColSize = pRandomSegment->getUsablePageSize() / 8;
00620
00621
00622 tupleDesc.push_back(
00623 TupleAttributeDescriptor(
00624 stdTypeFactory.newDataType(STANDARD_TYPE_VARBINARY), true,
00625 varColSize));
00626 tupleDesc.push_back(
00627 TupleAttributeDescriptor(
00628 stdTypeFactory.newDataType(STANDARD_TYPE_VARBINARY), true,
00629 varColSize));
00630 }
00631
00632 void LbmLoadBitmapTest::testCaseSetUp()
00633 {
00634 ExecStreamUnitTestBase::testCaseSetUp();
00635
00636 attrDesc_int64 = TupleAttributeDescriptor(
00637 stdTypeFactory.newDataType(STANDARD_TYPE_INT_64));
00638 }
00639
00640 void LbmLoadBitmapTest::testCaseTearDown()
00641 {
00642 for (uint i = 0; i < bTreeClusters.size(); i++) {
00643 bTreeClusters[i]->segmentAccessor.reset();
00644 }
00645 for (uint i = 0; i < bTreeBitmaps.size(); i++) {
00646 bTreeBitmaps[i]->segmentAccessor.reset();
00647 }
00648 bTreeClusters.clear();
00649 bTreeBitmaps.clear();
00650 savedBTreeClusterRootIds.clear();
00651 savedBTreeBitmapRootIds.clear();
00652 entryDumps.clear();
00653
00654 ExecStreamUnitTestBase::testCaseTearDown();
00655 }
00656
00657 FENNEL_UNIT_TEST_SUITE(LbmLoadBitmapTest);
00658
00659