diff --git a/cwCommon.h b/cwCommon.h index ea70dd8..89ec2bc 100644 --- a/cwCommon.h +++ b/cwCommon.h @@ -42,7 +42,9 @@ namespace cw kDuplicateRC, // 23 - an invalid duplicate was detected kAssertFailRC, // 24 - used with cwLogFatal kInvalidDataTypeRC, // 25 - kBaseAppRC // 26 + kFileNotFoundRC, // 26 + kTestFailRC, // 27 + kBaseAppRC // 28 } cwRC_t; typedef unsigned rc_t; diff --git a/cwCommonImpl.h b/cwCommonImpl.h index 7818640..0dee861 100644 --- a/cwCommonImpl.h +++ b/cwCommonImpl.h @@ -27,6 +27,47 @@ #define cwStringNullGuard(s) ((s)==nullptr ? "" : (s)) + // Perform byte swapping on 16 bit values. +#define cwSwap16(x) \ + (((((unsigned short)(x)) & 0x00ff) << 8) | ((((unsigned short)(x)) & 0xff00) >> 8)) + +#ifdef OS_LINUX +#include // gcc specific +#include + + // Perform byte swapping on 32 bit values on systems were is available. +#define cwSwap32(x) (bswap_32(x)) + + // Perform byte swapping on 64 bit values on systems were is available. +#define cwSwap64(x) (bswap_64(x)) + + +#endif + + +#ifdef OS_OSX +#include + + // Perform byte swapping on 32 bit values on systems were is not available. +#define cwSwap32(x) \ + ((((unsigned)((x) & 0x000000FF)) << 24) | \ + (((unsigned)((x) & 0x0000FF00)) << 8) | \ + (((unsigned)((x) & 0x00FF0000)) >> 8) | \ + (((unsigned)((x) & 0xFF000000)) >> 24)) + + // Perform byte swapping on 64 bit values on systems were is not available. +#define cwSwap64(x) \ + (((((unsigned long long)(x))<<56) & 0xFF00000000000000ULL) | \ + ((((unsigned long long)(x))<<40) & 0x00FF000000000000ULL) | \ + ((((unsigned long long)(x))<<24) & 0x0000FF0000000000ULL) | \ + ((((unsigned long long)(x))<< 8) & 0x000000FF00000000ULL) | \ + ((((unsigned long long)(x))>> 8) & 0x00000000FF000000ULL) | \ + ((((unsigned long long)(x))>>24) & 0x0000000000FF0000ULL) | \ + ((((unsigned long long)(x))>>40) & 0x000000000000FF00ULL) | \ + ((((unsigned long long)(x))>>56) & 0x00000000000000FFULL)) + +#endif + #define cwAllFlags(f,m) (((f) & (m)) == (m)) // Test if all of a group 'm' of binary flags in 'f' are set. #define cwIsFlag(f,m) (((f) & (m)) ? true : false) // Test if any one of a the bits in 'm' is also set in 'f'. @@ -45,6 +86,9 @@ #define cwCountOf(a) (sizeof(a)/sizeof(a[0])) + + + // Taken from here: // https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s // and here: @@ -91,6 +135,23 @@ namespace cw #define cwAssert(cond) while(1){ if(!(cond)){ cwLogFatal(kAssertFailRC,"Assert failed on condition:%s",#cond ); } break; } + + + + + template< typename T> + bool is_int(const T& x) + { return false; } + + template<> inline bool is_int( const signed char& x ) { return true; } + template<> inline bool is_int( const unsigned char& x ) { return true; } + template<> inline bool is_int( const signed short& x ) { return true; } + template<> inline bool is_int( const unsigned short& x ) { return true; } + template<> inline bool is_int( const signed long& x ) { return true; } + template<> inline bool is_int( const unsigned long& x ) { return true; } + template<> inline bool is_int( const signed long long& x ) { return true; } + template<> inline bool is_int( const unsigned long long& x ) { return true; } + template< typename H, typename T > diff --git a/cwFileSys.cpp b/cwFileSys.cpp index dffe1a1..8b8f24e 100644 --- a/cwFileSys.cpp +++ b/cwFileSys.cpp @@ -656,3 +656,10 @@ cw::filesys::dirEntry_t* cw::filesys::dirEntries( const char* dirStr, unsigned f return r.rp; } +cw::rc_t cw::filesys::makeDir( const char* dirStr ) +{ + if( mkdir(dirStr, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 ) + return cwLogSysError( kOpFailRC, errno, "The attempt to create the directory '%s' failed.",dirStr); + + return kOkRC; +} diff --git a/cwFileSys.h b/cwFileSys.h index 3f57792..724eeb4 100644 --- a/cwFileSys.h +++ b/cwFileSys.h @@ -25,6 +25,7 @@ namespace cw char* makeFn( const char* dir, const char* fn, const char* ext, ... ); + // The returned string must be released by a call to mem::release() or mem::free(). char* expandPath( const char* dir ); @@ -74,7 +75,9 @@ namespace cw // number of records in the returned array. dirEntry_t* dirEntries( const char* dirStr, unsigned includeFlags, unsigned* dirEntryCntRef ); - + + + rc_t makeDir( const char* dirStr ); } } diff --git a/cwLog.cpp b/cwLog.cpp index 201ea38..29fbe76 100644 --- a/cwLog.cpp +++ b/cwLog.cpp @@ -22,6 +22,7 @@ namespace cw idLabelPair_t logLevelLabelArray[] = { + { kPrint_LogLevel, "" }, { kDebug_LogLevel, "debug" }, { kInfo_LogLevel, "info" }, { kWarning_LogLevel, "warning" }, @@ -168,16 +169,29 @@ void cw::log::defaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* ou if( level < kWarning_LogLevel ) rcStr = ""; - // levelStr, msg,sys_msg, rc, function, lineno, filename - const char* fmt = "%s: %s %s %s %s\n"; + if( level == kPrint_LogLevel ) + { + const char* fmt = "%s"; + int n = snprintf(nullptr,0,fmt,msg); + cwAssert(n != -1); + char s[n+1]; + int m = snprintf(s,n+1,fmt,msg); + cwAssert(m==n); + outFunc(outCbArg,level,s); + } + else + { + // levelStr, msg,sys_msg, rc, function, lineno, filename + const char* fmt = "%s: %s %s %s %s\n"; - int n = snprintf(nullptr,0,fmt,levelStr,msg,syStr,rcStr,loStr); - cwAssert(n != -1); - char s[n+1]; - int m = snprintf(s,n+1,fmt,levelStr,msg,syStr,rcStr,loStr); - cwAssert(m==n); - - outFunc(outCbArg,level,s); + int n = snprintf(nullptr,0,fmt,levelStr,msg,syStr,rcStr,loStr); + cwAssert(n != -1); + char s[n+1]; + int m = snprintf(s,n+1,fmt,levelStr,msg,syStr,rcStr,loStr); + cwAssert(m==n); + outFunc(outCbArg,level,s); + } + } diff --git a/cwLog.h b/cwLog.h index 5f55d2a..e782296 100644 --- a/cwLog.h +++ b/cwLog.h @@ -9,6 +9,7 @@ namespace cw typedef enum { kInvalid_LogLevel, + kPrint_LogLevel, kDebug_LogLevel, kInfo_LogLevel, kWarning_LogLevel, @@ -50,6 +51,9 @@ namespace cw #define cwLogVDebugH(h,rc,fmt, vl) cw::log::msg( h, cw::log::kDebug_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl ) #define cwLogDebugH( h,rc,fmt,...) cw::log::msg( h, cw::log::kDebug_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ ) +#define cwLogVPrintH(h,fmt, vl) cw::log::msg( h, cw::log::kPrint_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, cw::kOkRC, fmt, vl ) +#define cwLogPrintH( h,fmt,...) cw::log::msg( h, cw::log::kPrint_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, cw::kOkRC, fmt, ##__VA_ARGS__ ) + #define cwLogVInfoH(h,fmt, vl) cw::log::msg( h, cw::log::kInfo_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, cw::kOkRC, fmt, vl ) #define cwLogInfoH( h,fmt,...) cw::log::msg( h, cw::log::kInfo_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, cw::kOkRC, fmt, ##__VA_ARGS__ ) @@ -83,6 +87,8 @@ namespace cw #endif +#define cwLogVPrint(fmt, vl) cwLogVPrintH( cw::log::globalHandle(), (fmt), (vl) ) +#define cwLogPrint( fmt,...) cwLogPrintH( cw::log::globalHandle(), (fmt), ##__VA_ARGS__ ) #define cwLogVInfo(fmt, vl) cwLogVInfoH( cw::log::globalHandle(), (fmt), (vl) ) #define cwLogInfo( fmt,...) cwLogInfoH( cw::log::globalHandle(), (fmt), ##__VA_ARGS__ ) diff --git a/cwMtx.h b/cwMtx.h index 2bfb647..d44f679 100644 --- a/cwMtx.h +++ b/cwMtx.h @@ -1,6 +1,28 @@ #ifndef cwMtx_h #define cwMtx_h +/* +Memory Layout: + +3x2 mtx: + +0 3 +1 4 +2 5 + +3x2x2 mtx + +front back +0 3 | 6 9 +1 4 | 7 10 +2 5 | 8 11 + + +More about dope and weight vectors. +https://stackoverflow.com/questions/30409991/use-a-dope-vector-to-access-arbitrary-axial-slices-of-a-multidimensional-array + + */ + namespace cw { namespace mtx @@ -19,22 +41,31 @@ namespace cw unsigned flags = 0; unsigned dimN = 0; unsigned* dimV = nullptr; + unsigned* mulV = nullptr; T* base = nullptr; unsigned allocEleN = 0; // always 0 if data is aliased - }; + }; + + template< typename T > + void release( struct mtx_str& m ) + { + mem::release(m.dimV); + if( cwIsNotFlag(m.flags,kAliasNoReleaseFl) ) + mem::release(m.base); + } + template< typename T > void release( struct mtx_str*& m ) { if( m != nullptr ) { - mem::release(m->dimV); - if( cwIsNotFlag(m->flags,kAliasNoReleaseFl) ) - mem::release(m->base); + release(*m); mem::release(m); } } + // Note that dimV[] is always copied and therefore is the reponsibility of the caller to free. template< typename T > struct mtx_str* _init( struct mtx_str* m, unsigned dimN, const unsigned* dimV, T* base=nullptr, unsigned flags=0 ) { @@ -42,20 +73,24 @@ namespace cw if( m == nullptr ) m = mem::allocZ>(1); - // if the pre-allocd mtx obj has more dim's then the new one + // if the pre-allocd mtx obj has more dim's than the new one if( m->dimN >= dimN ) m->dimN = dimN; else // else expand dimV[] { - m->dimV = mem::resize(m->dimV,dimN); + m->dimV = mem::resize(m->dimV,dimN*2); + m->mulV = m->dimV + dimN; m->dimN = dimN; } // update dimV[] with the new extents and calc. the new ele count unsigned eleN = 0; + unsigned mul = 1; for(unsigned i=0; idimV[i] = dimV[i]; + m->mulV[i] = mul; + mul *= dimV[i]; eleN = (i==0 ? 1 : eleN) * dimV[i]; } @@ -123,7 +158,87 @@ namespace cw struct mtx_str* allocAliasNoRelease( unsigned dimN, const unsigned* dimV, const T* base ) { return _init( nullptr, dimN, dimV, const_cast(base), kAliasNoReleaseFl); } + + unsigned _offsetDimV( const unsigned* dimV, unsigned dimN, unsigned* idxV ); + unsigned _offsetMulV( const unsigned* dimV, unsigned dimN, unsigned* idxV ); + unsigned _mtx_object_get_degree( const struct object_str* cfg ); + rc_t _mtx_object_get_shape( const struct object_str* cfg, unsigned i, unsigned* dimV, unsigned dimN, unsigned& eleN ); + + // 'i' is the index into 'idxV[]' of the matrix dimension which 'cfg' refers to + template< typename T> + rc_t _get_mtx_eles_from_cfg( const struct object_str* cfg, struct mtx_str* m, unsigned i, unsigned* idxV ) + { + rc_t rc = kOkRC; + + // if cfg is not a list then this must be a value + if( !cfg->is_list() ) + { + // get the value + T v; + if(cfg->value(v) != kOkRC ) + return cwLogError(kSyntaxErrorRC,"Unable to obtain matrix value in dimension index: %i\n",i); + + // and store it in the current idxV[] location + m->base[ _offsetMulV(m->mulV,m->dimN,idxV) ] = v; + + return kOkRC; + } + + // otherwise this is a list - and the list must contain lists or values + for(unsigned j=0; jchild_count(); ++j) + { + // update idxV[] which the dimension of the ith child elment + idxV[i] = j; + + // recurse! + if((rc = _get_mtx_eles_from_cfg(cfg->child_ele(j) ,m, i+1, idxV)) != kOkRC ) + break; + } + + + return rc; + } + + template< typename T > + struct mtx_str* allocCfg( const struct object_str* cfg ) + { + unsigned dimN = 0; + + // get the degree of the matrix + dimN = _mtx_object_get_degree(cfg); + + // if 'cfg' does not refer to a matrix + if( dimN == 0 ) + { + cwLogError(kSyntaxErrorRC,"The matrix object does not have a list-list syntax."); + } + else + { + // allocate the shape vector + unsigned dimV[dimN]; + unsigned idxV[dimN]; + unsigned eleN = 0; + struct mtx_str* m = nullptr; + + // get the shape of the matrix + if( _mtx_object_get_shape(cfg,0,dimV,dimN,eleN) != kOkRC ) + return nullptr; + + // allocate the matrix + if((m = alloc(dimN,dimV)) == nullptr ) + cwLogError(kObjAllocFailRC,"A matrix allocation failed."); + else + // + if(_get_mtx_eles_from_cfg(cfg,m,0,idxV) == kOkRC ) + return m; + + } + + return nullptr; + + } + template< typename T > struct mtx_str* alloc( unsigned dimN, const unsigned* dimV, T* base=nullptr, unsigned flags=0 ) { return _init( nullptr, dimN, dimV, base, flags); } @@ -139,6 +254,63 @@ namespace cw struct mtx_str* resize( struct mtx_str* y, const struct mtx_str& x ) { return resize(y,x->dimV,x->dimN); } + + template< typename T > + unsigned offset( const struct mtx_str* m, const unsigned* idxV ) + { + unsigned offset = 0; + for(unsigned i=0; idimN; ++i) + offset += idxV[i] * m->mulV[i]; + + return offset; + } + + + template< typename T > + unsigned _offset( const struct mtx_str* m, int i, unsigned offs ) + { return offs; } + + template< typename T, typename... ARGS> + unsigned _offset( const struct mtx_str* m, int i, unsigned offs, unsigned idx, ARGS&&... args) + { return _offset(m,i+1, offs + idx*m->mulV[i], std::forward(args)...); } + + template< typename T, typename... ARGS> + unsigned offset( const struct mtx_str* m, unsigned idx, ARGS&&... args) + { return _offset(m,0,0,idx,std::forward(args)...); } + + template< typename T, typename... ARGS> + unsigned offset( const struct mtx_str& m, unsigned idx, ARGS&&... args) + { return _offset(&m,0,0,idx,std::forward(args)...); } + + template< typename T > + T* addr( const struct mtx_str* m, const unsigned* idxV ) + { return m->base + offset(m,idxV); } + + template< typename T, typename... ARGS> + T* addr( struct mtx_str* m, unsigned i, ARGS&&... args) + { return m->base + offset(m,i,std::forward(args)...); } + + template< typename T > + T& ele( const struct mtx_str* m, const unsigned* idxV ) + { return *addr(m,idxV); } + + template< typename T, typename... ARGS> + T& ele( struct mtx_str* m, unsigned i, ARGS&&... args) + { return *addr(m,i,std::forward(args)...); } + + + template< typename T > + bool is_col_vector( const struct mtx_str& m ) + { return m->dimN==1 || (m->dimN==2 && m->dimV[1]==1); }; + + template< typename T > + bool is_row_vector( const struct mtx_str& m ) + { return m->dimN==2 && m->dimV[0]==1; } + + template< typename T > + bool is_vector( const struct mtx_str& m ) + { return is_col_vector(m) || is_row_vector(m); } + // Return 'true' if the matrices have the same size. template< typename T > bool is_size_equal( const struct mtx_str& x0, const struct mtx_str& x1 ) @@ -146,22 +318,112 @@ namespace cw if( x0.dimN != x1.dimN ) return false; - for(unsigned i=0; idimN; ++i) + for(unsigned i=0; i + bool is_equal( const struct mtx_str& x0, const struct mtx_str& x1 ) + { + if( !is_size_equal(x0,x1) ) + return false; + unsigned N = ele_count(x0); + for(unsigned i=0; i - bool ele_count( const struct mtx_str& x ) + unsigned ele_count( const struct mtx_str& x ) { unsigned eleN = 1; - for(unsigned i=0; i + void transpose( struct mtx_str& m ) + { + for(unsigned i=0; i + void _print( const struct mtx_str& m, unsigned* idxV, unsigned i, unsigned decPl, unsigned colWidth ) + { + if( i == m.dimN ) + { + double v = ele( &m, idxV ); + + // print the value + printf("%*.*f ",colWidth,decPl,v); + } + else + { + for(unsigned j=0; j=2 && i == m.dimN-2 ) + { + // print the dimension index for matrices with 3+ dim's + if( i > 0 && j == 0 ) + printf("%i\n",idxV[i-1]); + + // print the row index for matrices with 2+ dim's + if( m.dimN>1 ) + printf("%i | ",j); + } + + idxV[i] = j; + _print(m, idxV, i+1, decPl, colWidth ); + } + + // prevent multiple newlines on last printed line + if( m.dimN==1 || (m.dimN>=2 && i > m.dimN-2) ) + printf("\n"); + } + } + + template< typename T > + void print( const struct mtx_str& m, unsigned decPl=3, unsigned colWidth=10 ) + { + unsigned idxV[ m.dimN ]; + memset(idxV,0,sizeof(idxV)); + + if( is_int(*m.base) ) + decPl = 0; + + _print( m, idxV, 0, decPl, colWidth ); + } + + template< typename T > + void report( const struct mtx_str& m, const char* label, unsigned decPl=3, unsigned colWidth=10 ) + { + printf("%s :",label); + for(unsigned i=0; i @@ -244,15 +506,60 @@ namespace cw } - template< typename T > - void mtx_mul( struct mtx_str& y, const struct mtx_str& m, const struct mtx_str& x ) + template< typename T0, typename T1 > + rc_t mtx_mul( struct mtx_str& y, const struct mtx_str& m, const struct mtx_str& x ) { + assert( x.dimN >= 1 && m.dimN >= 1 ); + + unsigned xrn = x.dimN==1 ? ele_count(x) : x.dimV[0]; + unsigned xcn = x.dimN==1 ? 1 : x.dimV[1]; + unsigned mrn = m.dimN==1 ? 1 : m.dimV[0]; + unsigned mcn = m.dimN==1 ? ele_count(m) : m.dimV[1]; + unsigned yDimV[] = { mrn, xcn }; + if( mcn != xrn ) + return cwLogError(kInvalidArgRC, "Mtx mult. failed. Size mismatch: m[%i,%i] x[%i,%i].",mrn,mcn,xrn,xcn); + + //printf("%i %i : %i %i\n",mrn,mcn,xrn,xcn); + + resize(&y,yDimV, 2 ); + + // go across the columns of x + for(unsigned i=0; i fmtx_t; + typedef struct mtx_str f_t; + typedef struct mtx_str d_t; + rc_t test( const struct object_str* cfg ); } diff --git a/cwObject.h b/cwObject.h index 5a6f5f8..66765e5 100644 --- a/cwObject.h +++ b/cwObject.h @@ -131,7 +131,8 @@ namespace cw rc_t value( bool& v ) const; rc_t value( char*& v ) const; rc_t value( const char*& v ) const; - + rc_t value( const struct object_str*& v) const {v=this; return kOkRC; } + const char* pair_label() const; const struct object_str* pair_value() const; @@ -143,6 +144,10 @@ namespace cw const struct object_str* find( const char* label, unsigned flags=0 ) const; struct object_str* find( const char* label, unsigned flags=0 ); + const struct object_str* find_child( const char* label ) const { return find(label,kNoRecurseFl); } + struct object_str* find_child( const char* label ) { return find(label,kNoRecurseFl); } + + const struct object_str* child_ele( unsigned idx ) const; struct object_str* child_ele( unsigned idx ); @@ -185,7 +190,7 @@ namespace cw unsigned to_string( char* buf, unsigned bufByteN ) const; void print(const print_ctx_t* c=NULL) const; struct object_str* duplicate() const; - + } object_t; object_t* newObject( std::uint8_t v, object_t* parent=nullptr); @@ -221,6 +226,7 @@ namespace cw rc_t objectFromFile( const char* fn, object_t*& objRef ); void objectPrintTypes( object_t* o ); + } #endif diff --git a/cwUtility.cpp b/cwUtility.cpp index d7f3449..8c54282 100644 --- a/cwUtility.cpp +++ b/cwUtility.cpp @@ -35,3 +35,89 @@ void cw::printHex( const void* buf, unsigned bufByteN, bool asciiFl ) } } } + + +// TODO: rewrite to avoid copying +// this code comes via csound source ... +double cw::x80ToDouble( unsigned char rate[10] ) +{ + char sign; + short exp = 0; + unsigned long mant1 = 0; + unsigned long mant0 = 0; + double val; + unsigned char* p = (unsigned char*)rate; + + exp = *p++; + exp <<= 8; + exp |= *p++; + sign = (exp & 0x8000) ? 1 : 0; + exp &= 0x7FFF; + + mant1 = *p++; + mant1 <<= 8; + mant1 |= *p++; + mant1 <<= 8; + mant1 |= *p++; + mant1 <<= 8; + mant1 |= *p++; + + mant0 = *p++; + mant0 <<= 8; + mant0 |= *p++; + mant0 <<= 8; + mant0 |= *p++; + mant0 <<= 8; + mant0 |= *p++; + + /* special test for all bits zero meaning zero + - else pow(2,-16383) bombs */ + if (mant1 == 0 && mant0 == 0 && exp == 0 && sign == 0) + return 0.0; + else { + val = ((double)mant0) * pow(2.0,-63.0); + val += ((double)mant1) * pow(2.0,-31.0); + val *= pow(2.0,((double) exp) - 16383.0); + return sign ? -val : val; + } +} + +// TODO: rewrite to avoid copying +/* + * Convert double to IEEE 80 bit floating point + * Should be portable to all C compilers. + * 19aug91 aldel/dpwe covered for MSB bug in Ultrix 'cc' + */ + +void cw::doubleToX80(double val, unsigned char rate[10]) +{ + char sign = 0; + short exp = 0; + unsigned long mant1 = 0; + unsigned long mant0 = 0; + unsigned char* p = (unsigned char*)rate; + + if (val < 0.0) { sign = 1; val = -val; } + + if (val != 0.0) /* val identically zero -> all elements zero */ + { + exp = (short)(std::log(val)/std::log(2.0) + 16383.0); + val *= pow(2.0, 31.0+16383.0-(double)exp); + mant1 =((unsigned)val); + val -= ((double)mant1); + val *= pow(2.0, 32.0); + mant0 =((double)val); + } + + *p++ = ((sign<<7)|(exp>>8)); + *p++ = (u_char)(0xFF & exp); + *p++ = (u_char)(0xFF & (mant1>>24)); + *p++ = (u_char)(0xFF & (mant1>>16)); + *p++ = (u_char)(0xFF & (mant1>> 8)); + *p++ = (u_char)(0xFF & (mant1)); + *p++ = (u_char)(0xFF & (mant0>>24)); + *p++ = (u_char)(0xFF & (mant0>>16)); + *p++ = (u_char)(0xFF & (mant0>> 8)); + *p++ = (u_char)(0xFF & (mant0)); + +} diff --git a/cwUtility.h b/cwUtility.h index 5285717..88d5d00 100644 --- a/cwUtility.h +++ b/cwUtility.h @@ -4,6 +4,9 @@ namespace cw { void printHex( const void* buf, unsigned bufByteN, bool asciiFl=true ); + + double x80ToDouble( unsigned char s[10] ); + void doubleToX80( double v, unsigned char s[10] ); }