File indexing completed on 2026-04-09 07:49:36
0001 #pragma once
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036 #include <cassert>
0037 #include <vector>
0038 #include <cstring>
0039 #include <string>
0040 #include <cstdlib>
0041
0042 #include "scuda.h"
0043 #include "squad.h"
0044
0045 #ifdef WITH_SCUDA_DOUBLE
0046 #include "squad_double.h"
0047 #include "scuda_double.h"
0048 #endif
0049
0050 #include "sqat4.h"
0051 #include "stran.h"
0052 #include "spath.h"
0053 #include "sphoton.h"
0054
0055 #include "sfr.h"
0056
0057 #include "NP.hh"
0058
0059
0060 struct sframe
0061 {
0062 static constexpr const bool VERBOSE = false ;
0063 static constexpr const char* NAME = "sframe" ;
0064 static constexpr const char* DEFAULT_FRS = "-1" ;
0065 static constexpr const char* DEFAULT_NAME = "ALL" ;
0066
0067 static constexpr const unsigned NUM_4x4 = 4 ;
0068 static constexpr const unsigned NUM_VALUES = NUM_4x4*4*4 ;
0069 static constexpr const float EPSILON = 1e-5 ;
0070
0071 float4 ce = {} ;
0072 quad q1 = {} ;
0073 quad q2 = {} ;
0074 quad q3 = {} ;
0075
0076 qat4 m2w = {} ;
0077 qat4 w2m = {} ;
0078
0079 quad4 aux = {} ;
0080
0081
0082
0083
0084
0085 const char* frs = nullptr ;
0086 Tran<double>* tr_m2w = nullptr ;
0087 Tran<double>* tr_w2m = nullptr ;
0088
0089
0090 const char* ek = nullptr ;
0091 const char* ev = nullptr ;
0092 const char* ekvid = nullptr ;
0093 const char* tree = nullptr ;
0094 const char* dynamic = nullptr ;
0095
0096 sframe();
0097 sframe(const sframe& other);
0098 ~sframe();
0099 void cleanup();
0100
0101 sfr spawn_lite() const ;
0102 void populate( const sfr& l );
0103
0104 void zero() ;
0105 bool is_zero() const ;
0106
0107 std::string desc() const ;
0108
0109 static sframe Load( const char* dir, const char* name=NAME);
0110 static sframe Load_(const char* path );
0111 static sframe Fabricate(float tx=0.f, float ty=0.f, float tz=0.f);
0112 static int PopulateFromRaw(sframe& fr, const char* raw, char delim);
0113
0114
0115
0116 void set_grid(const std::vector<int>& cegs, float gridscale);
0117 int ix0() const ;
0118 int ix1() const ;
0119 int iy0() const ;
0120 int iy1() const ;
0121 int iz0() const ;
0122 int iz1() const ;
0123 int num_photon() const ;
0124 float gridscale() const ;
0125
0126 void set_tree( const char* _tree );
0127 void set_dynamic( const char* _dynamic );
0128
0129 void set_ekv( const char* k ) ;
0130 void set_ekv( const char* k, const char* v ) ;
0131 const char* form_ekvid() const ;
0132 const char* getFrameId() const ;
0133
0134 const char* get_frs() const ;
0135 bool is_frs_default() const ;
0136
0137 const char* get_name() const ;
0138
0139 void set_midx_mord_gord(int midx, int mord, int gord);
0140 int midx() const ;
0141 int mord() const ;
0142 int gord() const ;
0143
0144
0145
0146
0147 void set_inst(int inst);
0148 int inst() const ;
0149
0150 void set_identity(int ins, int gas, int sensor_identifier, int sensor_index );
0151 int ins() const ;
0152 int gas() const ;
0153 int sensor_identifier() const ;
0154 int sensor_index() const ;
0155
0156 void set_propagate_epsilon(float eps);
0157 void set_hostside_simtrace();
0158
0159 float propagate_epsilon() const ;
0160 bool is_hostside_simtrace() const ;
0161
0162
0163 float* data() ;
0164 const float* cdata() const ;
0165
0166 void write( float* dst, unsigned num_values ) const ;
0167 NP* getFrameArray() const ;
0168 void save(const char* dir, const char* name=NAME) const ;
0169 void save_extras(const char* dir) ;
0170
0171
0172 void read( const float* src, unsigned num_values ) ;
0173 void load(const char* dir, const char* name=NAME) ;
0174 void load_(const char* path ) ;
0175 void load(const NP* a) ;
0176
0177
0178
0179 void prepare();
0180
0181 NP* transform_photon_m2w( const NP* ph, bool normalize=true ) const ;
0182 NP* transform_photon_w2m( const NP* ph, bool normalize=true ) const ;
0183 NP* transform_photon( const NP* ph, bool normalize, bool inverse ) const ;
0184
0185
0186 void transform_m2w( sphoton& p, bool normalize=true ) const ;
0187 void transform_w2m( sphoton& p, bool normalize=true ) const ;
0188
0189
0190 Tran<double>* getTransform() const ;
0191
0192
0193 void setTranslate(float x, float y, float z);
0194 void setTransform(const qat4* m2w_ );
0195
0196 void set_m2w(const glm::tmat4x4<double>& g_m2w);
0197 void set_w2m(const glm::tmat4x4<double>& g_w2m);
0198
0199 };
0200
0201
0202
0203 inline sframe::sframe()
0204 :
0205 tr_m2w(nullptr),
0206 tr_w2m(nullptr)
0207 {
0208 #ifdef SFRAME_DEBUG
0209 printf("//sframe::sframe ctor NULL: tr_m2w %p tr_w2m %p \n", tr_m2w, tr_w2m );
0210 #endif
0211 }
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225 inline sframe::sframe(const sframe& other)
0226 {
0227 ce = other.ce ;
0228 q1 = other.q1 ;
0229 q2 = other.q2 ;
0230 q3 = other.q3 ;
0231 m2w = other.m2w ;
0232 w2m = other.w2m ;
0233 aux = other.aux ;
0234
0235 frs = other.frs ? strdup(other.frs) : nullptr ;
0236
0237 tr_m2w = nullptr ;
0238 tr_w2m = nullptr ;
0239 prepare();
0240
0241 ek = other.ek ? strdup(other.ek) : nullptr ;
0242 ev = other.ev ? strdup(other.ev) : nullptr ;
0243 ekvid = other.ekvid ? strdup(other.ekvid) : nullptr ;
0244 tree = other.tree ? strdup(other.tree) : nullptr ;
0245 dynamic = other.dynamic ? strdup(other.dynamic) : nullptr ;
0246
0247
0248 #ifdef SFRAME_DEBUG
0249 printf("//sframe.copy.ctor NEW: tr_m2w %p tr_w2m %p \n", tr_m2w, tr_w2m );
0250 #endif
0251 }
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261 inline sfr sframe::spawn_lite() const
0262 {
0263 sfr l ;
0264 l.ce.x = ce.x ;
0265 l.ce.y = ce.y ;
0266 l.ce.z = ce.z ;
0267 l.ce.w = ce.w ;
0268
0269 l.m2w = tr_m2w->t ;
0270 l.w2m = tr_w2m->t ;
0271
0272 return l ;
0273 }
0274
0275 inline void sframe::populate( const sfr& l )
0276 {
0277 set_m2w(l.m2w);
0278 set_w2m(l.w2m);
0279
0280 ce.x = l.ce.x ;
0281 ce.y = l.ce.y ;
0282 ce.z = l.ce.z ;
0283 ce.w = l.ce.w ;
0284 }
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295 inline sframe::~sframe()
0296 {
0297 #ifdef SFRAME_DEBUG
0298 printf("//sframe::~sframe tr_m2w %p tr_w2m %p \n", tr_m2w, tr_w2m );
0299 #endif
0300
0301 }
0302
0303 inline void sframe::cleanup()
0304 {
0305 free((void*)frs);
0306 delete tr_m2w ;
0307 delete tr_w2m ;
0308 free((void*)ek);
0309 free((void*)ev);
0310 free((void*)ekvid);
0311 }
0312
0313
0314 inline void sframe::zero()
0315 {
0316 ce = {} ;
0317 q1 = {} ;
0318 q2 = {} ;
0319 q3 = {} ;
0320 m2w = {} ;
0321 w2m = {} ;
0322 aux = {} ;
0323
0324 frs = nullptr ;
0325 tr_m2w = nullptr ;
0326 tr_w2m = nullptr ;
0327 ek = nullptr ;
0328 ev = nullptr ;
0329 ekvid = nullptr ;
0330 tree = nullptr ;
0331 dynamic = nullptr ;
0332 }
0333
0334
0335 inline bool sframe::is_zero() const
0336 {
0337 return ce.x == 0. && ce.y == 0. && ce.z == 0. && ce.w == 0. ;
0338 }
0339
0340
0341
0342
0343 inline std::string sframe::desc() const
0344 {
0345 std::stringstream ss ;
0346 ss << "sframe::desc"
0347 << " inst " << inst()
0348 << " frs " << ( frs ? frs : "-" ) << std::endl
0349 << " tree " << ( tree ? tree : "-" ) << std::endl
0350 << " dynamic " << ( dynamic ? dynamic : "-" ) << std::endl
0351 << " ekvid " << ( ekvid ? ekvid : "-" )
0352 << " ek " << ( ek ? ek : "-" )
0353 << " ev " << ( ev ? ev : "-" )
0354 << std::endl
0355 << " ce " << ce
0356 << " is_zero " << is_zero()
0357 << std::endl
0358 << " m2w " << m2w
0359 << std::endl
0360 << " w2m " << w2m
0361 << std::endl
0362 << " midx " << std::setw(4) << midx()
0363 << " mord " << std::setw(4) << mord()
0364 << " gord " << std::setw(4) << gord()
0365 << std::endl
0366 << " inst " << std::setw(4) << inst()
0367 << std::endl
0368 << " ix0 " << std::setw(4) << ix0()
0369 << " ix1 " << std::setw(4) << ix1()
0370 << " iy0 " << std::setw(4) << iy0()
0371 << " iy1 " << std::setw(4) << iy1()
0372 << " iz0 " << std::setw(4) << iz0()
0373 << " iz1 " << std::setw(4) << iz1()
0374 << " num_photon " << std::setw(4) << num_photon()
0375 << std::endl
0376 << " ins " << std::setw(4) << ins()
0377 << " gas " << std::setw(4) << gas()
0378 << " sensor_identifier " << std::setw(7) << sensor_identifier()
0379 << " sensor_index " << std::setw(5) << sensor_index()
0380 << std::endl
0381 << " propagate_epsilon " << std::setw(10) << std::fixed << std::setprecision(5) << propagate_epsilon()
0382 << " is_hostside_simtrace " << ( is_hostside_simtrace() ? "YES" : "NO" )
0383 << std::endl
0384 ;
0385 std::string str = ss.str();
0386 return str ;
0387 }
0388
0389
0390 inline sframe sframe::Load(const char* dir, const char* name)
0391 {
0392 sframe fr ;
0393 fr.load(dir, name);
0394 return fr ;
0395 }
0396 inline sframe sframe::Load_(const char* path)
0397 {
0398 sframe fr ;
0399 fr.load_(path);
0400 return fr ;
0401 }
0402
0403
0404
0405
0406
0407
0408
0409 inline sframe sframe::Fabricate(float tx, float ty, float tz)
0410 {
0411 sframe fr ;
0412 fr.setTranslate(tx, ty, tz) ;
0413 fr.prepare();
0414 return fr ;
0415 }
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426 inline int sframe::PopulateFromRaw(sframe& fr, const char* raw, char delim )
0427 {
0428
0429
0430
0431 std::vector<double> elem ;
0432 sstr::split<double>( elem, raw, delim );
0433 int num_elem = elem.size();
0434
0435 std::cout
0436 << "sframe::PopulateFromRaw"
0437 << " raw [" << ( raw ? raw : "-" ) << "]"
0438 << " num_elem " << num_elem
0439 << " elem " << sstr::desc<double>(elem)
0440 << "\n"
0441 ;
0442
0443 if( num_elem == 4 || num_elem == 3 || num_elem == 2 || num_elem == 1 )
0444 {
0445 float tx = num_elem > 0 ? elem[0] : 0.f ;
0446 float ty = num_elem > 1 ? elem[1] : 0.f ;
0447 float tz = num_elem > 2 ? elem[2] : 0.f ;
0448 float extent = num_elem > 3 ? elem[3] : 1000.f ;
0449
0450 fr.setTranslate( tx, ty, tz );
0451 fr.ce.w = extent ;
0452 }
0453 else
0454 {
0455 std::cerr
0456 << "sframe::PopulateFromRaw"
0457 << " UNHANDLED num_elem " << num_elem
0458 << " raw [" << ( raw ? raw : "-" ) << "]"
0459 << "\n" ;
0460 assert(0);
0461 std::raise(SIGINT);
0462 }
0463 fr.prepare();
0464 return 0;
0465 }
0466
0467
0468
0469
0470 inline void sframe::set_grid(const std::vector<int>& cegs, float gridscale)
0471 {
0472 assert( cegs.size() == 8 );
0473
0474 q1.i.x = cegs[0] ;
0475 q1.i.y = cegs[1] ;
0476 q1.i.z = cegs[2] ;
0477 q1.i.w = cegs[3] ;
0478
0479 q2.i.x = cegs[4] ;
0480 q2.i.y = cegs[5] ;
0481 q2.i.z = cegs[6] ;
0482 q2.f.w = gridscale ;
0483
0484
0485
0486 }
0487
0488 inline int sframe::ix0() const { return q1.i.x ; }
0489 inline int sframe::ix1() const { return q1.i.y ; }
0490 inline int sframe::iy0() const { return q1.i.z ; }
0491 inline int sframe::iy1() const { return q1.i.w ; }
0492 inline int sframe::iz0() const { return q2.i.x ; }
0493 inline int sframe::iz1() const { return q2.i.y ; }
0494 inline int sframe::num_photon() const { return q2.i.z ; }
0495 inline float sframe::gridscale() const { return q2.f.w ; }
0496
0497
0498 inline void sframe::set_tree( const char* _tree )
0499 {
0500 tree = _tree ? strdup(_tree) : nullptr ;
0501 }
0502 inline void sframe::set_dynamic( const char* _dynamic )
0503 {
0504 dynamic = _dynamic ? strdup(_dynamic) : nullptr ;
0505 }
0506
0507
0508
0509
0510 inline void sframe::set_ekv( const char* k )
0511 {
0512 char* v = getenv(k) ;
0513 set_ekv(k, v);
0514 }
0515
0516 inline void sframe::set_ekv( const char* k, const char* v )
0517 {
0518 ek = k ? strdup(k) : nullptr ;
0519 ev = v ? strdup(v) : nullptr ;
0520 ekvid = form_ekvid();
0521 }
0522
0523 inline const char* sframe::form_ekvid() const
0524 {
0525 std::stringstream ss ;
0526 ss << "sframe_"
0527 << ( ek ? ek : "ek" )
0528 << "_"
0529 ;
0530 for(int i=0 ; i < int(ev?strlen(ev):0) ; i++) ss << ( ev[i] == ':' ? '_' : ev[i] ) ;
0531 std::string str = ss.str();
0532 return strdup(str.c_str());
0533 }
0534 inline const char* sframe::getFrameId() const
0535 {
0536 return ekvid ;
0537 }
0538
0539
0540
0541 inline const char* sframe::get_frs() const
0542 {
0543 return is_frs_default() ? nullptr : frs ;
0544 }
0545 inline bool sframe::is_frs_default() const
0546 {
0547 return frs != nullptr && strcmp(frs, DEFAULT_FRS) == 0 ;
0548 }
0549 inline const char* sframe::get_name() const
0550 {
0551 const char* f = get_frs();
0552 return f ? f : DEFAULT_NAME ;
0553 }
0554
0555 inline void sframe::set_midx_mord_gord(int midx, int mord, int gord)
0556 {
0557 q3.i.x = midx ;
0558 q3.i.y = mord ;
0559 q3.i.z = gord ;
0560 }
0561 inline int sframe::midx() const { return q3.i.x ; }
0562 inline int sframe::mord() const { return q3.i.y ; }
0563 inline int sframe::gord() const { return q3.i.z ; }
0564 inline int sframe::inst() const { return q3.i.w ; }
0565
0566 inline void sframe::set_inst(int inst){ q3.i.w = inst ; }
0567
0568 inline void sframe::set_identity(int ins, int gas, int sensor_identifier, int sensor_index )
0569 {
0570 aux.q0.i.x = ins ;
0571 aux.q0.i.y = gas ;
0572 aux.q0.i.z = sensor_identifier ;
0573 aux.q0.i.w = sensor_index ;
0574 }
0575 inline int sframe::ins() const { return aux.q0.i.x ; }
0576 inline int sframe::gas() const { return aux.q0.i.y ; }
0577 inline int sframe::sensor_identifier() const { return aux.q0.i.z ; }
0578 inline int sframe::sensor_index() const { return aux.q0.i.w ; }
0579
0580
0581 inline void sframe::set_propagate_epsilon(float eps){ aux.q1.f.x = eps ; }
0582 inline void sframe::set_hostside_simtrace(){ aux.q1.u.y = 1u ; }
0583
0584 inline float sframe::propagate_epsilon() const { return aux.q1.f.x ; }
0585 inline bool sframe::is_hostside_simtrace() const { return aux.q1.u.y == 1u ; }
0586
0587
0588 inline const float* sframe::cdata() const
0589 {
0590 return (const float*)&ce.x ;
0591 }
0592 inline float* sframe::data()
0593 {
0594 return (float*)&ce.x ;
0595 }
0596 inline void sframe::write( float* dst, unsigned num_values ) const
0597 {
0598 assert( num_values == NUM_VALUES );
0599 char* dst_bytes = (char*)dst ;
0600 char* src_bytes = (char*)cdata();
0601 unsigned num_bytes = sizeof(float)*num_values ;
0602 memcpy( dst_bytes, src_bytes, num_bytes );
0603 }
0604
0605 inline void sframe::read( const float* src, unsigned num_values )
0606 {
0607 assert( num_values == NUM_VALUES );
0608 char* src_bytes = (char*)src ;
0609 char* dst_bytes = (char*)data();
0610 unsigned num_bytes = sizeof(float)*num_values ;
0611 memcpy( dst_bytes, src_bytes, num_bytes );
0612 }
0613
0614 inline NP* sframe::getFrameArray() const
0615 {
0616 NP* a = NP::Make<float>(NUM_4x4, 4, 4) ;
0617 write( a->values<float>(), NUM_4x4*4*4 ) ;
0618
0619 a->set_meta<std::string>("creator", "sframe::getFrameArray");
0620 if(frs) a->set_meta<std::string>("frs", frs);
0621 if(tree) a->set_meta<std::string>("tree", tree);
0622 if(dynamic) a->set_meta<std::string>("dynamic", dynamic);
0623 if(ek) a->set_meta<std::string>("ek", ek);
0624 if(ev) a->set_meta<std::string>("ev", ev);
0625 if(ekvid) a->set_meta<std::string>("ekvid", ekvid);
0626
0627 return a ;
0628 }
0629 inline void sframe::save(const char* dir, const char* name_ ) const
0630 {
0631 if(VERBOSE) std::cout
0632 << "[ sframe::save "
0633 << " dir : " << ( dir ? dir : "MISSING_DIR" )
0634 << " name: " << ( name_ ? name_ : "MISSING_NAME" )
0635 << std::endl
0636 ;
0637
0638 std::string name = U::form_name( name_ , ".npy" ) ;
0639 NP* a = getFrameArray();
0640
0641 a->save(dir, name.c_str());
0642
0643 if(VERBOSE) std::cout
0644 << "] sframe::save "
0645 << std::endl
0646 ;
0647
0648 }
0649 inline void sframe::save_extras(const char* dir)
0650 {
0651 if(tr_m2w == nullptr) prepare();
0652 tr_m2w->save(dir, "m2w.npy");
0653 tr_w2m->save(dir, "w2m.npy");
0654 }
0655
0656
0657 inline void sframe::load(const char* dir, const char* name_ )
0658 {
0659 std::string name = U::form_name( name_ , ".npy" ) ;
0660 const NP* a = NP::Load(dir, name.c_str() );
0661 load(a);
0662 }
0663 inline void sframe::load_(const char* path_)
0664 {
0665 const NP* a = NP::Load(path_);
0666 if(!a) std::cerr
0667 << "sframe::load_ ERROR : non-existing"
0668 << " path_ " << path_
0669 << std::endl
0670 ;
0671 assert(a);
0672 load(a);
0673 }
0674 inline void sframe::load(const NP* a)
0675 {
0676 read( a->cvalues<float>() , NUM_VALUES );
0677 std::string _frs = a->get_meta<std::string>("frs", "");
0678 std::string _tree = a->get_meta<std::string>("tree", "");
0679 std::string _dynamic = a->get_meta<std::string>("dynamic", "");
0680
0681 if(!_frs.empty()) frs = strdup(_frs.c_str());
0682 if(!_tree.empty()) tree = strdup(_tree.c_str());
0683 if(!_dynamic.empty()) dynamic = strdup(_dynamic.c_str());
0684 }
0685
0686
0687
0688
0689
0690
0691
0692
0693
0694
0695 inline void sframe::prepare()
0696 {
0697 tr_m2w = Tran<double>::ConvertFromQat(&m2w) ;
0698 tr_w2m = Tran<double>::ConvertFromQat(&w2m) ;
0699 }
0700
0701
0702
0703
0704
0705
0706
0707
0708
0709
0710
0711
0712
0713
0714 inline NP* sframe::transform_photon_m2w( const NP* ph, bool normalize ) const
0715 {
0716 bool inverse = false ;
0717 return transform_photon(ph, normalize, inverse);
0718 }
0719 inline NP* sframe::transform_photon_w2m( const NP* ph, bool normalize ) const
0720 {
0721 bool inverse = true ;
0722 return transform_photon(ph, normalize, inverse);
0723 }
0724
0725 inline NP* sframe::transform_photon( const NP* ph, bool normalize, bool inverse ) const
0726 {
0727 if( ph == nullptr ) return nullptr ;
0728 Tran<double>* tr = inverse ? tr_w2m : tr_m2w ;
0729 if(!tr) std::cerr << "sframe::transform_photon MUST sframe::prepare before calling sframe::transform_photon " << std::endl;
0730 assert( tr ) ;
0731 NP* pht = Tran<double>::PhotonTransform(ph, normalize, tr, false );
0732 assert( pht->ebyte == 8 );
0733 return pht ;
0734 }
0735
0736 inline void sframe::transform_m2w( sphoton& p, bool normalize ) const
0737 {
0738 if(!tr_m2w) std::cerr << "sframe::transform_m2w MUST sframe::prepare before calling this " << std::endl;
0739 assert( tr_m2w) ;
0740 p.transform( tr_m2w->t, normalize );
0741 }
0742
0743 inline void sframe::transform_w2m( sphoton& p, bool normalize ) const
0744 {
0745 if(!tr_w2m) std::cerr << "sframe::transform_w2m MUST sframe::prepare before calling this " << std::endl;
0746 assert( tr_w2m) ;
0747 p.transform( tr_w2m->t, normalize );
0748 }
0749
0750
0751 inline Tran<double>* sframe::getTransform() const
0752 {
0753 double eps = 1e-3 ;
0754 Tran<double>* geotran = Tran<double>::FromPair( &m2w, &w2m, eps );
0755 return geotran ;
0756 }
0757
0758 inline void sframe::setTranslate(float x, float y, float z)
0759 {
0760 qat4 m2w(x,y,z);
0761 setTransform(&m2w);
0762 }
0763
0764 inline void sframe::setTransform(const qat4* m2w_ )
0765 {
0766 const qat4* w2m_ = Tran<double>::Invert(m2w_);
0767 qat4::copy(m2w, *m2w_ );
0768 qat4::copy(w2m, *w2m_ );
0769 }
0770
0771
0772 inline void sframe::set_m2w(const glm::tmat4x4<double>& g_m2w)
0773 {
0774 const qat4* q_m2w = Tran<double>::ConvertFrom(g_m2w);
0775 qat4::copy(m2w, *q_m2w);
0776 }
0777 inline void sframe::set_w2m(const glm::tmat4x4<double>& g_w2m)
0778 {
0779 const qat4* q_w2m = Tran<double>::ConvertFrom(g_w2m);
0780 qat4::copy(w2m, *q_w2m);
0781 }
0782
0783
0784
0785
0786
0787
0788
0789
0790
0791
0792
0793
0794
0795 inline std::ostream& operator<<(std::ostream& os, const sframe& fr)
0796 {
0797 os << fr.desc() ;
0798 return os;
0799 }
0800
0801