From 064f758723b15f6a2451d56a9dc0ee8f3650f474 Mon Sep 17 00:00:00 2001 From: geay Date: Wed, 16 Apr 2014 11:24:04 +0200 Subject: [PATCH] correction of bug EDF 7972. Revealed in MEDReader. The problem happens with a field on profile on structured mesh is not a structured support. --- src/MEDLoader/MEDFileField.cxx | 20 ++++++++++++------- src/MEDLoader/MEDFileField.hxx | 4 ++-- src/MEDLoader/MEDFileFieldOverView.cxx | 27 ++++++++++++++++++++------ src/MEDLoader/MEDFileFieldOverView.hxx | 2 ++ src/MEDLoader/MEDFileMesh.cxx | 26 +++++++++++++++++++++---- src/MEDLoader/MEDFileMesh.hxx | 2 ++ src/MEDLoader/Swig/MEDLoaderTest3.py | 2 +- src/MEDLoader/Swig/MEDLoaderTest4.py | 4 ++-- 8 files changed, 65 insertions(+), 22 deletions(-) diff --git a/src/MEDLoader/MEDFileField.cxx b/src/MEDLoader/MEDFileField.cxx index 31812cb58..e7ef086c9 100644 --- a/src/MEDLoader/MEDFileField.cxx +++ b/src/MEDLoader/MEDFileField.cxx @@ -280,13 +280,19 @@ void MEDFileFieldPerMeshPerTypePerDisc::assignFieldNoProfile(int& start, int off * \b WARNING if not null the MED file profile can be subdivided again in case of Gauss points. * \param [in] mesh is the mesh coming from the MEDFileMesh instance in correspondance with the MEDFileField. The mesh inside the \a field is simply ignored. */ -void MEDFileFieldPerMeshPerTypePerDisc::assignFieldProfile(int& start, const DataArrayInt *multiTypePfl, const DataArrayInt *idsInPfl, DataArrayInt *locIds, int nbOfEltsInWholeMesh, const MEDCouplingFieldDouble *field, const DataArray *arrr, const MEDCouplingMesh *mesh, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc) +void MEDFileFieldPerMeshPerTypePerDisc::assignFieldProfile(bool isPflAlone, int& start, const DataArrayInt *multiTypePfl, const DataArrayInt *idsInPfl, DataArrayInt *locIds, int nbOfEltsInWholeMesh, const MEDCouplingFieldDouble *field, const DataArray *arrr, const MEDCouplingMesh *mesh, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc) { _profile.clear(); _type=field->getTypeOfField(); std::string pflName(multiTypePfl->getName()); std::ostringstream oss; oss << pflName; - if(_type!=ON_NODES) { const INTERP_KERNEL::CellModel& cm=INTERP_KERNEL::CellModel::GetCellModel(getGeoType()); oss << "_" << cm.getRepr(); } else { oss << "_NODE"; } + if(_type!=ON_NODES) + { + if(!isPflAlone) + { const INTERP_KERNEL::CellModel& cm=INTERP_KERNEL::CellModel::GetCellModel(getGeoType()); oss << "_" << cm.getRepr(); } + } + else + { oss << "_NODE"; } if(locIds) { if(pflName.empty()) @@ -1024,11 +1030,11 @@ void MEDFileFieldPerMeshPerType::assignFieldNoProfile(int& start, int offset, in * \param [in] nbOfEltsInWholeMesh nb of elts of type \a this->_geo_type in \b WHOLE mesh * \param [in] mesh is the mesh coming from the MEDFileMesh instance in correspondance with the MEDFileField. The mesh inside the \a field is simply ignored. */ -void MEDFileFieldPerMeshPerType::assignFieldProfile(int& start, const DataArrayInt *multiTypePfl, const DataArrayInt *idsInPfl, DataArrayInt *locIds, int nbOfEltsInWholeMesh, const MEDCouplingFieldDouble *field, const DataArray *arr, const MEDCouplingMesh *mesh, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc) +void MEDFileFieldPerMeshPerType::assignFieldProfile(bool isPflAlone, int& start, const DataArrayInt *multiTypePfl, const DataArrayInt *idsInPfl, DataArrayInt *locIds, int nbOfEltsInWholeMesh, const MEDCouplingFieldDouble *field, const DataArray *arr, const MEDCouplingMesh *mesh, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc) { std::vector pos=addNewEntryIfNecessary(field,idsInPfl); for(std::vector::const_iterator it=pos.begin();it!=pos.end();it++) - _field_pm_pt_pd[*it]->assignFieldProfile(start,multiTypePfl,idsInPfl,locIds,nbOfEltsInWholeMesh,field,arr,mesh,glob,nasc); + _field_pm_pt_pd[*it]->assignFieldProfile(isPflAlone,start,multiTypePfl,idsInPfl,locIds,nbOfEltsInWholeMesh,field,arr,mesh,glob,nasc); } void MEDFileFieldPerMeshPerType::assignNodeFieldNoProfile(int& start, const MEDCouplingFieldDouble *field, const DataArray *arr, MEDFileFieldGlobsReal& glob) @@ -1045,7 +1051,7 @@ void MEDFileFieldPerMeshPerType::assignNodeFieldProfile(int& start, const DataAr throw INTERP_KERNEL::Exception("MEDFileFieldPerMeshPerType::assignNodeFieldProfile : input array is null, or not allocated !"); _field_pm_pt_pd.resize(1); _field_pm_pt_pd[0]=MEDFileFieldPerMeshPerTypePerDisc::New(this,ON_NODES,-3); - _field_pm_pt_pd[0]->assignFieldProfile(start,pfl,pfl2,pfl2,-1,field,arr,0,glob,nasc);//mesh is not requested so 0 is send. + _field_pm_pt_pd[0]->assignFieldProfile(true,start,pfl,pfl2,pfl2,-1,field,arr,0,glob,nasc);//mesh is not requested so 0 is send. } std::vector MEDFileFieldPerMeshPerType::addNewEntryIfNecessary(const MEDCouplingFieldDouble *field, int offset, int nbOfCells) @@ -1619,7 +1625,7 @@ void MEDFileFieldPerMesh::assignFieldNoProfileNoRenum(int& start, const std::vec */ void MEDFileFieldPerMesh::assignFieldProfile(int& start, const DataArrayInt *multiTypePfl, const std::vector& code, const std::vector& code2, const std::vector& idsInPflPerType, const std::vector& idsPerType, const MEDCouplingFieldDouble *field, const DataArray *arr, const MEDCouplingMesh *mesh, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc) { - int nbOfTypes=code.size()/3; + int nbOfTypes(code.size()/3); for(int i=0;iassignFieldProfile(start,multiTypePfl,idsInPflPerType[i],pfl,code2[3*found+1],field,arr,mesh,glob,nasc); + _field_pm_pt[pos]->assignFieldProfile(nbOfTypes==1,start,multiTypePfl,idsInPflPerType[i],pfl,code2[3*found+1],field,arr,mesh,glob,nasc); } } diff --git a/src/MEDLoader/MEDFileField.hxx b/src/MEDLoader/MEDFileField.hxx index 818270091..f912ccee7 100644 --- a/src/MEDLoader/MEDFileField.hxx +++ b/src/MEDLoader/MEDFileField.hxx @@ -104,7 +104,7 @@ namespace ParaMEDMEM std::vector getDirectChildren() const; MEDFileFieldPerMeshPerTypePerDisc *deepCpy(MEDFileFieldPerMeshPerType *father) const; void assignFieldNoProfile(int& start, int offset, int nbOfCells, const MEDCouplingFieldDouble *field, const DataArray *arrr, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc); - void assignFieldProfile(int& start, const DataArrayInt *multiTypePfl, const DataArrayInt *idsInPfl, DataArrayInt *locIds, int nbOfEltsInWholeMesh, const MEDCouplingFieldDouble *field, const DataArray *arrr, const MEDCouplingMesh *mesh, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc); + void assignFieldProfile(bool isPflAlone, int& start, const DataArrayInt *multiTypePfl, const DataArrayInt *idsInPfl, DataArrayInt *locIds, int nbOfEltsInWholeMesh, const MEDCouplingFieldDouble *field, const DataArray *arrr, const MEDCouplingMesh *mesh, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc); void assignNodeFieldNoProfile(int& start, const MEDCouplingFieldDouble *field, const DataArray *arrr, MEDFileFieldGlobsReal& glob); void getCoarseData(TypeOfField& type, std::pair& dad, std::string& pfl, std::string& loc) const; void writeLL(med_idt fid, const MEDFileFieldNameScope& nasc) const; @@ -180,7 +180,7 @@ namespace ParaMEDMEM std::vector getDirectChildren() const; MEDFileFieldPerMeshPerType *deepCpy(MEDFileFieldPerMesh *father) const; void assignFieldNoProfile(int& start, int offset, int nbOfCells, const MEDCouplingFieldDouble *field, const DataArray *arr, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc); - void assignFieldProfile(int& start, const DataArrayInt *multiTypePfl, const DataArrayInt *idsInPfl, DataArrayInt *locIds, int nbOfEltsInWholeMesh, const MEDCouplingFieldDouble *field, const DataArray *arr, const MEDCouplingMesh *mesh, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc); + void assignFieldProfile(bool isPflAlone, int& start, const DataArrayInt *multiTypePfl, const DataArrayInt *idsInPfl, DataArrayInt *locIds, int nbOfEltsInWholeMesh, const MEDCouplingFieldDouble *field, const DataArray *arr, const MEDCouplingMesh *mesh, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc); void assignNodeFieldNoProfile(int& start, const MEDCouplingFieldDouble *field, const DataArray *arr, MEDFileFieldGlobsReal& glob); void assignNodeFieldProfile(int& start, const DataArrayInt *pfl, const MEDCouplingFieldDouble *field, const DataArray *arr, MEDFileFieldGlobsReal& glob, const MEDFileFieldNameScope& nasc); const MEDFileFieldPerMesh *getFather() const; diff --git a/src/MEDLoader/MEDFileFieldOverView.cxx b/src/MEDLoader/MEDFileFieldOverView.cxx index 521c4bd1b..47ec2a37e 100644 --- a/src/MEDLoader/MEDFileFieldOverView.cxx +++ b/src/MEDLoader/MEDFileFieldOverView.cxx @@ -668,7 +668,7 @@ MEDUMeshMultiLev *MEDUMeshMultiLev::New(const MEDFileUMesh *m, const std::vector return new MEDUMeshMultiLev(m,levs); } -MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector& levs):MEDMeshMultiLev(m) +MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector& levs):MEDMeshMultiLev(m),_is_holding_a_ref_already_held(true) { if(!m) throw INTERP_KERNEL::Exception("MEDUMeshMultiLev constructor : null input pointer !"); @@ -771,7 +771,7 @@ MEDUMeshMultiLev *MEDUMeshMultiLev::New(const MEDFileUMesh *m, const std::vector return new MEDUMeshMultiLev(m,gts,pfls,nbEntities); } -MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector& gts, const std::vector& pfls, const std::vector& nbEntities):MEDMeshMultiLev(m,m->getNumberOfNodes(),gts,pfls,nbEntities) +MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector& gts, const std::vector& pfls, const std::vector& nbEntities):MEDMeshMultiLev(m,m->getNumberOfNodes(),gts,pfls,nbEntities),_is_holding_a_ref_already_held(true) { std::size_t sz(gts.size()); if(sz<1) @@ -897,11 +897,11 @@ MEDMeshMultiLev *MEDUMeshMultiLev::prepare() const return new MEDUMeshMultiLev(*this); } -MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDUMeshMultiLev& other):MEDMeshMultiLev(other),_parts(other._parts),_coords(other._coords) +MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDUMeshMultiLev& other):MEDMeshMultiLev(other),_is_holding_a_ref_already_held(other._is_holding_a_ref_already_held),_parts(other._parts),_coords(other._coords) { } -MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDStructuredMeshMultiLev& other, const MEDCouplingAutoRefCountObjectPtr& part):MEDMeshMultiLev(other) +MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDStructuredMeshMultiLev& other, const MEDCouplingAutoRefCountObjectPtr& part):MEDMeshMultiLev(other),_is_holding_a_ref_already_held(false) { _parts.resize(1); _parts[0]=part; @@ -911,6 +911,7 @@ MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDStructuredMeshMultiLev& other, const } /*! + * To be called only once ! Because due to some optimizations (sometimes aggressive) the internal state can be changed... * If returned value is false output pointer \a coords is not the internal pointer. If returned value is true output pointer \a coords is directly the internal pointer. * If true is returned, the \a coords output parameter should be used with care (non const method call) to avoid to change the internal state of MEDFileUMesh instance. */ @@ -1063,12 +1064,26 @@ bool MEDUMeshMultiLev::buildVTUArrays(DataArrayDouble *& coords, DataArrayByte * reorderNodesIfNecessary(a,d,f); if(a->getNumberOfComponents()!=3) a=a->changeNbOfComponents(3,0.); - coords=a.retn(); types=b.retn(); cellLocations=c.retn(); cells=d.retn(); + coords=a.retn(); + if(!_is_holding_a_ref_already_held) + { + if(tmp==((DataArrayDouble *)a)) + { + if(_parts.empty()) + { const_cast(this)->_coords=0; } + else + { const_cast(this)->_parts[0]->setCoords(0); } + } + } + types=b.retn(); cellLocations=c.retn(); cells=d.retn(); if(!isPolyh) { faceLocations=0; faces=0; } else { faceLocations=e.retn(); faces=f.retn(); } - return tmp==((DataArrayDouble *)a); + if(_is_holding_a_ref_already_held) + return tmp==((DataArrayDouble *)a); + else + return false; } void MEDUMeshMultiLev::reorderNodesIfNecessary(MEDCouplingAutoRefCountObjectPtr& coords, DataArrayInt *nodalConnVTK, DataArrayInt *polyhedNodalConnVTK) const diff --git a/src/MEDLoader/MEDFileFieldOverView.hxx b/src/MEDLoader/MEDFileFieldOverView.hxx index 84dfed483..0cc6f2bf4 100644 --- a/src/MEDLoader/MEDFileFieldOverView.hxx +++ b/src/MEDLoader/MEDFileFieldOverView.hxx @@ -147,6 +147,8 @@ namespace ParaMEDMEM MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector& levs); MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector& gts, const std::vector& pfls, const std::vector& nbEntities); private: + //! this boolean will desappear shortly. It is always equal to true except for the case where a partial cartesian mesh leads to a unstructured mesh. + bool _is_holding_a_ref_already_held; std::vector< MEDCouplingAutoRefCountObjectPtr > _parts; //! this attribute is used only for mesh with no cells but having coordinates. For classical umeshes those pointer is equal to pointer of coordinates of instances in this->_parts. MEDCouplingAutoRefCountObjectPtr _coords; diff --git a/src/MEDLoader/MEDFileMesh.cxx b/src/MEDLoader/MEDFileMesh.cxx index 6360c2a9e..c13172715 100644 --- a/src/MEDLoader/MEDFileMesh.cxx +++ b/src/MEDLoader/MEDFileMesh.cxx @@ -4698,7 +4698,11 @@ MEDCouplingMesh *MEDFileStructuredMesh::getGenMeshAtLevel(int meshDimRelToMax, b { if(!m) throw INTERP_KERNEL::Exception("MEDFileStructuredMesh::getGenMeshAtLevel : level -1 requested must be non empty to be able to compute unstructured sub mesh !"); - return _faces_if_necessary; + buildMinusOneImplicitPartIfNeeded(); + MEDCouplingMesh *ret(_faces_if_necessary); + if(ret) + ret->incrRef(); + return ret; } default: throw INTERP_KERNEL::Exception("MEDFileCurveLinearMesh does not support multi level for mesh 0 expected as input !"); @@ -4754,9 +4758,8 @@ int MEDFileStructuredMesh::buildImplicitPartIfAny(INTERP_KERNEL::NormalizedCellT const INTERP_KERNEL::CellModel& cm(INTERP_KERNEL::CellModel::GetCellModel(MEDCouplingStructuredMesh::GetGeoTypeGivenMeshDimension(getMeshDimension()))); if(cm.getReverseExtrudedType()!=gt) throw INTERP_KERNEL::Exception(MSG); - const MEDCouplingStructuredMesh *mcmesh(getStructuredMesh()); - _faces_if_necessary=mcmesh->build1SGTSubLevelMesh(); - return mcmesh->getNumberOfCellsOfSubLevelMesh(); + buildImplicitPart(); + return getStructuredMesh()->getNumberOfCellsOfSubLevelMesh(); } else { @@ -4766,6 +4769,21 @@ int MEDFileStructuredMesh::buildImplicitPartIfAny(INTERP_KERNEL::NormalizedCellT } } +void MEDFileStructuredMesh::buildMinusOneImplicitPartIfNeeded() const +{ + const MEDCoupling1SGTUMesh *zeFaceMesh(_faces_if_necessary); + if(!zeFaceMesh) + buildImplicitPart(); +} + +void MEDFileStructuredMesh::buildImplicitPart() const +{ + const MEDCouplingStructuredMesh *mcmesh(getStructuredMesh()); + if(!mcmesh) + throw INTERP_KERNEL::Exception("MEDFileStructuredMesh::buildImplicitPart : Unable to build the implicit part of structured mesh because no structured mesh at level 0 defined !"); + _faces_if_necessary=mcmesh->build1SGTSubLevelMesh(); +} + void MEDFileStructuredMesh::releaseImplicitPartIfAny() const { _faces_if_necessary=0; diff --git a/src/MEDLoader/MEDFileMesh.hxx b/src/MEDLoader/MEDFileMesh.hxx index 227f2beab..ca36d3f87 100644 --- a/src/MEDLoader/MEDFileMesh.hxx +++ b/src/MEDLoader/MEDFileMesh.hxx @@ -345,6 +345,8 @@ namespace ParaMEDMEM void deepCpyAttributes(); void loadStrMeshFromFile(MEDFileStrMeshL2 *strm, med_idt fid, const std::string& mName, int dt, int it, MEDFileMeshReadSelector *mrs); void writeStructuredLL(med_idt fid, const std::string& maa) const; + void buildImplicitPart() const; + void buildMinusOneImplicitPartIfNeeded() const; static med_geometry_type GetGeoTypeFromMeshDim(int meshDim); private: static void LoadStrMeshDAFromFile(med_idt fid, int meshDim, int dt, int it, const std::string& mName, MEDFileMeshReadSelector *mrs, diff --git a/src/MEDLoader/Swig/MEDLoaderTest3.py b/src/MEDLoader/Swig/MEDLoaderTest3.py index 22eb05efb..9bc14349c 100644 --- a/src/MEDLoader/Swig/MEDLoaderTest3.py +++ b/src/MEDLoader/Swig/MEDLoaderTest3.py @@ -2007,7 +2007,7 @@ class MEDLoaderTest(unittest.TestCase): pfl=DataArrayInt.Range(0,50,1) ; pfl.setName("pfl") fff.appendFieldProfile(f2,mm,0,pfl) self.assertIn(fff.getHeapMemorySize(),xrange(2178-130,2178+100+(10+2)*strMulFac)) - self.assertIn(fff.getProfile("pfl_NORM_QUAD4").getHeapMemorySize(),xrange(215-10,215+10+2*strMulFac)) + self.assertIn(fff.getProfile("pfl").getHeapMemorySize(),xrange(205-10,205+10+2*strMulFac)) self.assertIn(fff[1,-1].getHeapMemorySize(),xrange(700-50,700+30+4*strMulFac)) pass diff --git a/src/MEDLoader/Swig/MEDLoaderTest4.py b/src/MEDLoader/Swig/MEDLoaderTest4.py index 72e46da3f..a0006742d 100644 --- a/src/MEDLoader/Swig/MEDLoaderTest4.py +++ b/src/MEDLoader/Swig/MEDLoaderTest4.py @@ -4278,7 +4278,7 @@ class MEDLoaderTest4(unittest.TestCase): fs.pushField(fmts1) cc.write(fname,2) fs.write(fname,0) - ########## GO for reading in MEDReader,by not loading all. Mesh is fully loaded but not fields values + ########## GO for reading in MEDReader,by not loading all. Mesh is fully loaded but not fields values ms=MEDFileMeshes(fname) fields=MEDFileFields(fname,False) fields.removeFieldsWithoutAnyTimeStep() @@ -4328,7 +4328,7 @@ class MEDLoaderTest4(unittest.TestCase): mml2=mml.prepare() self.assertTrue(isinstance(mml2,MEDUMeshMultiLev)) # here UMesh is important ncc,a0,a1,a2,a3,a4,a5=mml2.buildVTUArrays() - self.assertTrue(ncc)# here all the nodes are taken directly without copy + self.assertTrue(not ncc)# BUG EDF 7972 ! here all the nodes are taken, BUT the coordinates comes from a non available array in file so in memory ! so false is returned ! self.assertTrue(a1.isEqual(DataArrayByte([9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9]))) self.assertTrue(a2.isEqual(DataArrayInt([0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100,105,110,115,120,125,130,135,140,145,150,155,160,165,170,175,180,185,190,195,200,205,210,215,220,225,230,235,240,245,250,255,260,265,270,275,280,285,290,295,300,305,310,315,320,325,330,335,340,345,350,355,360,365,370,375,380,385,390,395,400,405,410,415,420,425,430,435,440,445,450,455,460,465,470,475,480,485]))) self.assertTrue(a3.isEqual(DataArrayInt([4,0,12,15,3,4,12,24,27,15,4,24,36,39,27,4,36,48,51,39,4,3,15,18,6,4,15,27,30,18,4,27,39,42,30,4,39,51,54,42,4,6,18,21,9,4,18,30,33,21,4,30,42,45,33,4,42,54,57,45,4,1,13,16,4,4,13,25,28,16,4,25,37,40,28,4,37,49,52,40,4,4,16,19,7,4,16,28,31,19,4,28,40,43,31,4,40,52,55,43,4,7,19,22,10,4,19,31,34,22,4,31,43,46,34,4,43,55,58,46,4,2,14,17,5,4,14,26,29,17,4,26,38,41,29,4,38,50,53,41,4,5,17,20,8,4,17,29,32,20,4,29,41,44,32,4,41,53,56,44,4,8,20,23,11,4,20,32,35,23,4,32,44,47,35,4,44,56,59,47,4,0,12,13,1,4,12,24,25,13,4,24,36,37,25,4,36,48,49,37,4,1,13,14,2,4,13,25,26,14,4,25,37,38,26,4,37,49,50,38,4,3,15,16,4,4,15,27,28,16,4,27,39,40,28,4,39,51,52,40,4,4,16,17,5,4,16,28,29,17,4,28,40,41,29,4,40,52,53,41,4,6,18,19,7,4,18,30,31,19,4,30,42,43,31,4,42,54,55,43,4,7,19,20,8,4,19,31,32,20,4,31,43,44,32,4,43,55,56,44,4,9,21,22,10,4,21,33,34,22,4,33,45,46,34,4,45,57,58,46,4,10,22,23,11,4,22,34,35,23,4,34,46,47,35,4,46,58,59,47,4,0,1,4,3,4,3,4,7,6,4,6,7,10,9,4,1,2,5,4,4,4,5,8,7,4,7,8,11,10,4,12,13,16,15,4,15,16,19,18,4,18,19,22,21,4,13,14,17,16,4,16,17,20,19,4,19,20,23,22,4,24,25,28,27,4,27,28,31,30,4,30,31,34,33,4,25,26,29,28,4,28,29,32,31,4,31,32,35,34,4,36,37,40,39,4,39,40,43,42,4,42,43,46,45,4,37,38,41,40,4,40,41,44,43,4,43,44,47,46,4,48,49,52,51,4,51,52,55,54,4,54,55,58,57,4,49,50,53,52,4,52,53,56,55,4,55,56,59,58]))) -- 2.39.2