]> SALOME platform Git repositories - modules/med.git/commitdiff
Salome HOME
correction of bug EDF 7972. Revealed in MEDReader. The problem happens with a field...
authorgeay <anthony.geay@cea.fr>
Wed, 16 Apr 2014 09:24:04 +0000 (11:24 +0200)
committergeay <anthony.geay@cea.fr>
Wed, 16 Apr 2014 09:24:04 +0000 (11:24 +0200)
src/MEDLoader/MEDFileField.cxx
src/MEDLoader/MEDFileField.hxx
src/MEDLoader/MEDFileFieldOverView.cxx
src/MEDLoader/MEDFileFieldOverView.hxx
src/MEDLoader/MEDFileMesh.cxx
src/MEDLoader/MEDFileMesh.hxx
src/MEDLoader/Swig/MEDLoaderTest3.py
src/MEDLoader/Swig/MEDLoaderTest4.py

index 31812cb580399c189896548076994926718debba..e7ef086c98c0b3ef32a63b3e70b8883157002403 100644 (file)
@@ -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<int> pos=addNewEntryIfNecessary(field,idsInPfl);
   for(std::vector<int>::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<int> 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<int>& code, const std::vector<int>& code2, const std::vector<DataArrayInt *>& idsInPflPerType, const std::vector<DataArrayInt *>& 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;i<nbOfTypes;i++)
     {
       INTERP_KERNEL::NormalizedCellType type=(INTERP_KERNEL::NormalizedCellType)code[3*i];
@@ -1634,7 +1640,7 @@ void MEDFileFieldPerMesh::assignFieldProfile(int& start, const DataArrayInt *mul
           break;
       if(found==nbOfTupes2)
         throw INTERP_KERNEL::Exception("MEDFileFieldPerMesh::assignFieldProfile : internal problem ! Should never happen ! Please report bug to anthony.geay@cea.fr !");
-      _field_pm_pt[pos]->assignFieldProfile(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);
     }
 }
 
index 818270091f2219874fe496105f3d1629f00dd2c5..f912ccee7c202628edd17454830a89f010dc82e9 100644 (file)
@@ -104,7 +104,7 @@ namespace ParaMEDMEM
     std::vector<const BigMemoryObject *> 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<int,int>& 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<const BigMemoryObject *> 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;
index 521c4bd1bc46a6e10fb450fd2b815b56d23fe1ca..47ec2a37ea43f255a78d0d8002bd45f36ae18642 100644 (file)
@@ -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<int>& levs):MEDMeshMultiLev(m)
+MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector<int>& 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<INTERP_KERNEL::NormalizedCellType>& gts, const std::vector<const DataArrayInt *>& pfls, const std::vector<int>& nbEntities):MEDMeshMultiLev(m,m->getNumberOfNodes(),gts,pfls,nbEntities)
+MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector<INTERP_KERNEL::NormalizedCellType>& gts, const std::vector<const DataArrayInt *>& pfls, const std::vector<int>& 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<MEDCoupling1GTUMesh>& part):MEDMeshMultiLev(other)
+MEDUMeshMultiLev::MEDUMeshMultiLev(const MEDStructuredMeshMultiLev& other, const MEDCouplingAutoRefCountObjectPtr<MEDCoupling1GTUMesh>& 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<MEDUMeshMultiLev *>(this)->_coords=0; }
+          else
+            { const_cast<MEDUMeshMultiLev *>(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<DataArrayDouble>& coords, DataArrayInt *nodalConnVTK, DataArrayInt *polyhedNodalConnVTK) const
index 84dfed4830924630b084fca77299b3ea7f8733e2..0cc6f2bf454728a492d20666ec550b1b8d3950f8 100644 (file)
@@ -147,6 +147,8 @@ namespace ParaMEDMEM
     MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector<int>& levs);
     MEDUMeshMultiLev(const MEDFileUMesh *m, const std::vector<INTERP_KERNEL::NormalizedCellType>& gts, const std::vector<const DataArrayInt *>& pfls, const std::vector<int>& 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<MEDCoupling1GTUMesh> > _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<DataArrayDouble> _coords;
index 6360c2a9ed17168051b0aeadb24cf0b7eaae5c2f..c131727150484efc07d8493d528472e8897b55eb 100644 (file)
@@ -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;
index 227f2beab48b0698c5d4356d65010af58cb1d2fa..ca36d3f870ea5a29849b0d894a46ca7927de6a1a 100644 (file)
@@ -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,
index 22eb05efb69afefd1cfd99c586d3366d86e7f322..9bc14349ce4d6f0086d883bd684e53f81b601643 100644 (file)
@@ -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
 
index 72e46da3f94320cd613a12a3119b9da2f4101a47..a0006742db63924c73e27b4a24cacc17645fec74 100644 (file)
@@ -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])))