Closed gogyzzz closed 6 years ago
추가적으로 궁금한 사항들
@Kandelion @kooBH 요청 내용은 하나씩 새로운 issue로 올리도록 하겠습니다. 여기 내용은 최신 요청 내용과 다를 수 있습니다
dot 함수같은 경우엔 blas도 matrix를 돌려주는것이 아니라 스칼라값을 구해서 리턴해주고 있습니다. 그래도 다른 방식으로 구현한다면 정확히 어떤 연산이 이루어져야 하는 건가요
@Kandelion 1 x 1 matrix 가 나와야 할 것 같습니다. 원래 수학적으로 엄밀히 보면 스칼라가 나오는게 맞는 것 같은데...
@gogyzzz 음.. 그럼 리턴값이 MAT*이 되어야 한다는 말씀이신가요? 크기가 1 by 1인?
으으음 으으으음 으으으으음 생각해보니
벡터용 닷은 스칼라를 리턴하고 매트릭스용 맷멀은 1x1을 리턴해야 할 것 같네요 역시 따로따로 잇어야 하겟군요 그대로 두세요
On Wed, Jul 18, 2018, 12:40 PM Kandelion notifications@github.com wrote:
음.. 그럼 리턴값이 MAT*이 되어야 한다는 말씀이신가요? 크기가 1 by 1인?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/gogyzzz/iip_sph_pp/issues/37#issuecomment-405799150, or mute the thread https://github.com/notifications/unsubscribe-auth/AHw6d4s1uLPKhxJwaFfMZInNSlPQoF93ks5uHq5GgaJpZM4VOdz9 .
-- Haeyong Kwon Sogang University | Electronic engineering (P): 010-8422-0243 blog: https://gogyzzz.blogspot.kr/
@gogyzzz 위에 표에 sum() 함수가 있는데 blas에는 찾아보니 의외로 없네요...;; blas와 관계없이 별도로 구현을 할까요?
+ sdot, cdot, udot 차이점은 입력받는 값이
sdot | cdot | udot | |
---|---|---|---|
Input | MAT, MAT | MAT, CMAT | CMAT, CMAT |
Output | DTYPE | CTYPE | CTYPE |
이렇게 됩니다.
여러분 wolfram alpha를 잘 이용하면 좋을것 같습니다... 킹프램갓파...
@Kandelion @kooBH 본문 내용을 수정하였습니다.
제가 생각하는 함수는 kaldi library의 그것들과 많이 닮아 있습니다.
kaldi library는 음성인식에 가장 많이 사용되고 있습니다. 음성 데이터를 다룰 수 있는 루틴이 굉장히 많은 편이라 기본 단계는 이것만 참고해도 될 정도입니다.
그리고 kaldi가 c++ 라이브러리이긴 하지만 행렬 연산을 할 때 연산자 오버로딩을 사용하지 않고 함수를 호출하는 방식으로 만들어져 있습니다.
그래서 본문 내용에 kaldi에서 사용되는 기본 matrix 함수들을 추가해봤습니다.
일단은 grep 명령어로 void를 파싱해서 함수 이름만 가져온 것이기 때문에, 인자가 모두 나타나 있지 않은 함수도 있습니다.
그리고 불필요한 함수들은 지워야 하는데 일단은 참고용으로 그대로 가져다 넣었습니다.
더 자세한 내용을 보시려면
https://github.com/kaldi-asr/kaldi/tree/master/src 에서 matrix, base, cudamatrix, util을 보시면 됩니다. 개인적으로는 한번씩 살펴봤으면 합니다.
// from haeyong @Kandelion 참고
예상 기간일 뿐이고 실제로 의미는 딱히 없습니다. subrange 연산은 보류하겠습니다. ( 앞으로 subrange 연산이 정말 필요한 상황이 있을지 잘 모르겠음. 본혁이에게 submatmul 해보라고 한 이후 다른 함수들에 대해서도 적용하면 조합이 폭발한다는 생각이 듬... 실제 신호처리 알고리즘을 짤 때 맞닥뜨리면 생각하는 것으로 )
180xxx / 고도화 180xxx / backend 성능 테스트 180xxx / mkl, openblas, cublas backend ( cublas는 사실 08/31 전에 적용됐으면 좋겠지만 일단.. )
180831 / 1차 목표 deadline
180xxx / 예외처리 ( ASSERT는 경고만 하는지 중단도 하는지, 경고만 하는 경우, 중단하는 경우, 등등 ) 180xxx / 기능 테스트 (MATLAB 값과 비교. 값의 일치 기준 정하기) 2 days / 함수 형태, 이름 규칙 정하기 -> 이름통일 -> 문서화, README.md 최신화
7 days / 기능 구현( evd, svd, inverse, diagonal, trace, determinant ) 현재 / 최적화 하지 않은 native C로 모든 함수를 구현(진짜 구현만)
다음을 추가했습니다. google fast approx fftw
프로젝트에서 다루는 이슈이므로 닫겠습니다
지시 감독인데 방만 운영을 해서 막중한 책임을 느끼고 있습니다 명료한 가이드를 드리겠습니다
일정 (아래에서 위로 읽어주세요)
180xxx / 고도화 180xxx / backend 성능 테스트 180xxx / mkl, openblas, cublas backend ( cublas는 사실 08/31 전에 적용됐으면 좋겠지만 일단.. )
180831 / 1차 목표 deadline
180xxx / 예외처리 ( ASSERT는 경고만 하는지 중단도 하는지, 경고만 하는 경우, 중단하는 경우, 등등 ) 180xxx / 기능 테스트 (MATLAB 값과 비교. 값의 일치 기준 정하기) 2 days / 함수 형태, 이름 규칙 정하기 -> 이름통일 -> 문서화, README.md 최신화
7 days / 기능 구현( evd, svd, inverse, diagonal, trace, determinant )
180725 / cmake 지원 by 규래 현재 / 최적화 하지 않은 native C로 모든 함수를 구현(진짜 구현만)
함수 리스트
matrix
| name | representation | note | |---|---|---| | alloc_MAT | UINT,UINT,UINT | | | alloc_CMAT | UINT,UINT,UINT | | | zeros | UINT,(UINT,UINT) | | | czeros | UINT,(UINT,UINT) | | | set | MAT*,UINT,(UINT,UINT),DTYPE | | | cset | CMAT*,UINT,(UINT,UINT),DTYPE | | | fill | MAT*, DTYPE | | | cfill | CMAT*, DTYPE, DTYPE | re, im | | get | MAT*,UINT,(UINT,UINT) | | | cget | CMAT*,UINT,UINT,UINT | | | submat | MAT*, MAT*, ITER,ITER, (ITER,ITER, ITER,ITER) | | | csubmat | CMAT*, CMAT*, ITER,ITER, (ITER,ITER, ITER,ITER) | | | free_MAT | MAT* | | | free_CMAT | CMAT* | | | print | MAT* | | | cprint | CMAT* | | | print_sub | MAT*, ITER,ITER, (ITER,ITER, ITER,ITER) | ITER 사용법은 submat 참고 | | cprint_sub | CMAT*, ITER,ITER, (ITER,ITER, ITER,ITER) | | | ele_prod | | C = alpha * A .* B + beta * C (element-wise) | | ele_div | | ele_pow | MAT*, n | C = alpha * pow(A,n) + C | | ele_sqrt | | C = alpha * sqrt(A) + C | | bro_prod | | C = alpha * A ?* B + beta * C (element-wise) | | permute | | [matlab permute, reshape 참고](http://21go.blogspot.com/2011/04/matlab-reshape-permute.html) | | reshape | | | | inverse | C = alpha * A^-1 + beta * C | | | trace | | | | diagonal | | |wave (iip_wav.h)
| name | representation | note | |---|---|---| | read_WAV | char* -> WAV* | | | write_WAV | WAV*, char* | | | WAV2MAT | WAV* -> MAT* | | | MAT2WAV | MAT* -> UINT | | | WAV_BUF2MAT | WAV_BUF* -> MAT* | | | print_WAV | WAV* | | | free_WAV | WAV* | |binary/text save/load
| name | representation | note | |---|---|---| | load_mat | const char*, MAT*, bool | 이 [repo](https://github.com/gogyzzz/complex_double_binary_to_matlab)를 참고 | | save_mat | const char*, MAT*, bool | |BLAS lv. 1
| name | representation | note | |---|---|---| | axpy | DTYPE, MAT*, MAT* | | | caxpy | DTYPE, CMAT*, CMAT* | | | copy | MAT*, MAT* | | | ccopy | CMAT*, CMAT* | | | asum | MAT*, UINT inc | sum과 asum은 다르지 않았나 | | sum | MAT*, int idx_dimension | | csum | CMAT*, int idx_dimension | | acsum | CMAT*, UINT inc | | | sum | | 이거 왜 없어졌지 | | csum | | | | dot | MAT*, UINT xinc, MAT*, UINT yinc -> DTYPE | 리턴 방식이면 안됨. 결과 값은 스칼라로 나와서는 안됨(차원이 merge된 matrix이어야 함) | | sdot | | | | cdot | | | | udot | | ??? 이거 뭐지 | | nrm2 | | | | rot | OpenBLAS 에는 csrot,zdrot이 없습니다| Plane rotation of points (??) | | scal | | | | swap | | | | iamax | | | | iamin | OpenBLAS 는 idamin,isamin 이 선언만 되어있고, 구현이 안되어있습니다| | | cabs1 | | | | rotm | | 무엇인지 모르겠습니다1 | | rotmg | | 무엇인지 모르겠습니다2 | | rotg | | 무엇인지 모르겠습니다3 | ```c // from kaldi/src struct dimension; void read_mat(const char* filename, MAT*); // dimension must be equal void read_cmat(const char* filename, CMAT*); void write_mat(const char* filename, MAT); void write_cmat(const char* filename, CMAT); #define IIP_ASSERT(condition) // assert function void iip_timer(); // needed void sqrt(MAT* ); void csqrt(CMAT* ); void pow(MAT*, DTYPE n); // exponent can be real value. void cpow(CMAT*, DTYPE n); // void upow(CMAT*, CTYPE n); // void randu(MAT*, DTYPE a, DTYPE b); // uniform distribution void crandu(CMAT*, DTYPE, ar, DTYPE br, DTYPE, ai, DTYPE bi); void randn(MAT*, DTYPE mean, DTYPE var); /// var or std 어느것으로 할 지느ㄴ 다른 라이브러리를 참고할 것 void crandn(CMAT*, CTYPE mean, CTYPE var); // C = AB + C // size(C) = size(A) >= size(B) void mul_elements(MAT* a, MAT* b, MAT* c); // with broadcasting void umul_elements(CMAT* a, CMAT* b, CMAT* c) // with broadcasting void div_elements(MAT* a, MAT* b, MAT* c) // with broadcasting void udiv_elements(CMAT* a, CMAT* b, CMAT* c) // with broadcasting void inv_elements(MAT* ) // (1 / element) void cinv_elements(CMAT* ) // (1 / element) void max(dimension*, MAT*); void cmax(dimension*, CMAT*); void amax(dimension* , MAT*); // absolute max dimension void camax(dimension* , CMAT*); // void min void round(MAT*); void floor void ceil void log(MAT*, UINT base); // base 타입은 알아보고 바꿀 것. void exp(MAT*, DTYPE exponent); void abs(MAT*) void repmat(MAT*, dimension*); // repeat matrix // https://www.mathworks.com/help/matlab/ref/repmat.html void reshape(MAT*, dimension*); void shiftdim(MAT*, SINT n); // shift // https://www.mathworks.com/help/matlab/ref/shiftdim.html void ???(MAT*, UINT dst0, UINT dst1, UINT dst2 ); // 3 x 2 x 1 -> 2 x 3 x 1 void svd // singular value decomposition void evd // eigen value decomposition // 아직 numerical recipe에서 못 찾음. 일단은 blas꺼를 그대로 쓰자. ```BLAS lv. 2, 3
### BLAS lv. 2 | name | representation | note | |---|---|---| | gemv | char transA,DTYPE,MAT*,MAT*,DTYPE,MAT* | Y = alpha * A * x + beta * Y | | cgemv | char transA, CTYPE,CMAT*,CMAT*,CTYPE,CMAT* | | ### BLAS lv. 3 | name | representation | note | |---|---|---| | matmul | MAT* A, MAT* B, MAT* C, | A * B -> C | | matmul_range | MAT* A, MAT* B, MAT* C, range* ra, range* rb, range* rc | | | aABpbC | DTYPE alpha, MAT* A, MAT* B, DTYPE beta, MAT* C | | | aABpbC | DTYPE alpha, MAT* A, MAT* B, DTYPE beta, MAT* C, range* ra, range* rb, range* rc | | | aAtBpbC | | | | aABtpbC | | sample | | caABpbC | | | | caAtBpbC | | | | caABtpbC | | | | caABhpbC | | | | caAhBpbC | | | | gemm | char transA, char transB, DTYPE, MAT*,MAT*,DTYPE,MAT* | C = alpha * A * B + beta * C | | cgemm | char, char, DTYPE re, DTYPE im, CMAT*,CMAT*,CTYPE,CMAT* | updated |reference
kaldi/src/base/
```c base/io-funcs-inl.h:templatekaldi matrix
```c cblas-wrappers.h:inline void cblas_Xcopy(const int N, const float *X, const int incX, float *Y, cblas-wrappers.h:inline void cblas_Xcopy(const int N, const double *X, const int incX, double *Y, cblas-wrappers.h:inline void cblas_Xrot(const int N, float *X, const int incX, float *Y, cblas-wrappers.h:inline void cblas_Xrot(const int N, double *X, const int incX, double *Y, cblas-wrappers.h:inline void cblas_Xaxpy(const int N, const float alpha, const float *X, cblas-wrappers.h:inline void cblas_Xaxpy(const int N, const double alpha, const double *X, cblas-wrappers.h:inline void cblas_Xscal(const int N, const float alpha, float *data, cblas-wrappers.h:inline void cblas_Xscal(const int N, const double alpha, double *data, cblas-wrappers.h:inline void cblas_Xspmv(const float alpha, const int num_rows, const float *Mdata, cblas-wrappers.h:inline void cblas_Xspmv(const double alpha, const int num_rows, const double *Mdata, cblas-wrappers.h:inline void cblas_Xtpmv(MatrixTransposeType trans, const float *Mdata, cblas-wrappers.h:inline void cblas_Xtpmv(MatrixTransposeType trans, const double *Mdata, cblas-wrappers.h:inline void cblas_Xtpsv(MatrixTransposeType trans, const float *Mdata, cblas-wrappers.h:inline void cblas_Xtpsv(MatrixTransposeType trans, const double *Mdata, cblas-wrappers.h:inline void cblas_Xspmv(MatrixIndexT dim, float alpha, const float *Mdata, cblas-wrappers.h:inline void cblas_Xspmv(MatrixIndexT dim, double alpha, const double *Mdata, cblas-wrappers.h:inline void cblas_Xspr2(MatrixIndexT dim, float alpha, const float *Xdata, cblas-wrappers.h:inline void cblas_Xspr2(MatrixIndexT dim, double alpha, const double *Xdata, cblas-wrappers.h:inline void cblas_Xspr(MatrixIndexT dim, float alpha, const float *Xdata, cblas-wrappers.h:inline void cblas_Xspr(MatrixIndexT dim, double alpha, const double *Xdata, cblas-wrappers.h:inline void cblas_Xgemv(MatrixTransposeType trans, MatrixIndexT num_rows, cblas-wrappers.h:inline void cblas_Xgemv(MatrixTransposeType trans, MatrixIndexT num_rows, cblas-wrappers.h:inline void cblas_Xgbmv(MatrixTransposeType trans, MatrixIndexT num_rows, cblas-wrappers.h:inline void cblas_Xgbmv(MatrixTransposeType trans, MatrixIndexT num_rows, cblas-wrappers.h:inline void Xgemv_sparsevec(MatrixTransposeType trans, MatrixIndexT num_rows, cblas-wrappers.h:inline void cblas_Xgemm(const float alpha, cblas-wrappers.h:inline void cblas_Xgemm(const double alpha, cblas-wrappers.h:inline void cblas_Xsymm(const float alpha, cblas-wrappers.h:inline void cblas_Xsymm(const double alpha, cblas-wrappers.h:inline void cblas_Xger(MatrixIndexT num_rows, MatrixIndexT num_cols, float alpha, cblas-wrappers.h:inline void cblas_Xger(MatrixIndexT num_rows, MatrixIndexT num_cols, double alpha, cblas-wrappers.h:inline void cblas_Xsyrk ( cblas-wrappers.h:inline void cblas_Xsyrk( cblas-wrappers.h:inline void cblas_Xsbmv1( cblas-wrappers.h:inline void cblas_Xsbmv1( cblas-wrappers.h:inline void mul_elements( cblas-wrappers.h:inline void mul_elements( cblas-wrappers.h:inline void clapack_Xtptri(KaldiBlasInt *num_rows, float *Mdata, KaldiBlasInt *result) { cblas-wrappers.h:inline void clapack_Xtptri(KaldiBlasInt *num_rows, double *Mdata, KaldiBlasInt *result) { cblas-wrappers.h:inline void clapack_Xgetrf2(KaldiBlasInt *num_rows, KaldiBlasInt *num_cols, cblas-wrappers.h:inline void clapack_Xgetrf2(KaldiBlasInt *num_rows, KaldiBlasInt *num_cols, cblas-wrappers.h:inline void clapack_Xgetri2(KaldiBlasInt *num_rows, float *Mdata, KaldiBlasInt *stride, cblas-wrappers.h:inline void clapack_Xgetri2(KaldiBlasInt *num_rows, double *Mdata, KaldiBlasInt *stride, cblas-wrappers.h:inline void clapack_Xgesvd(char *v, char *u, KaldiBlasInt *num_cols, cblas-wrappers.h:inline void clapack_Xgesvd(char *v, char *u, KaldiBlasInt *num_cols, cblas-wrappers.h:void inline clapack_Xsptri(KaldiBlasInt *num_rows, float *Mdata, cblas-wrappers.h:void inline clapack_Xsptri(KaldiBlasInt *num_rows, double *Mdata, cblas-wrappers.h:void inline clapack_Xsptrf(KaldiBlasInt *num_rows, float *Mdata, cblas-wrappers.h:void inline clapack_Xsptrf(KaldiBlasInt *num_rows, double *Mdata, cblas-wrappers.h:inline void clapack_Xgetrf(MatrixIndexT num_rows, MatrixIndexT num_cols, cblas-wrappers.h:inline void clapack_Xgetrf(MatrixIndexT num_rows, MatrixIndexT num_cols, cblas-wrappers.h:inline void clapack_Xgetri(MatrixIndexT num_rows, float *Mdata, MatrixIndexT stride, cblas-wrappers.h:inline void clapack_Xgetri(MatrixIndexT num_rows, double *Mdata, MatrixIndexT stride, compressed-matrix.h: void *Data() const { return this->data_; } compressed-matrix.h: void CopyFromMat(const MatrixBasekaldi cudamatrix
```c cudamatrix/cu-allocator.h: // shouldn't be too critical. The reason it exists is to avoid calling the cudamatrix/cu-allocator.h: void Check() { cudamatrix/cu-allocator.h: void* Malloc(size_t size); cudamatrix/cu-allocator.h: void* MallocPitch(size_t row_bytes, size_t num_rows, size_t *pitch); cudamatrix/cu-allocator.h: void Free(void *ptr); cudamatrix/cu-allocator.h: inline void* MallocLocking(size_t size) { cudamatrix/cu-allocator.h: inline void* MallocPitchLocking(size_t row_bytes, size_t num_rows, size_t *pitch) { cudamatrix/cu-allocator.h: void FreeLocking(void *ptr) { cudamatrix/cu-allocator.h: void PrintMemoryUsage() const; cudamatrix/cu-allocator.h: void FreeSomeCachedMemory(size_t bytes_to_free); cudamatrix/cu-allocator.h: inline void* MallocPitchInternal(size_t row_bytes, size_t num_rows, size_t *pitch); cudamatrix/cu-allocator.h: void *pointer; // the CUDA memory location that we own cudamatrix/cu-allocator.h: CachedMemoryElement(void *pointer, size_t t, size_t pitch): cudamatrix/cu-allocator.h: void Insert(const MemoryRequest &request, cudamatrix/cu-allocator.h: size_t operator() (const void *arg) const noexcept { cudamatrix/cu-allocator.h: unordered_mapkaldi util
```c util/basic-filebuf.h: void swap(basic_filebuf& rhs); util/basic-filebuf.h: void imbue(const std::locale& loc) override; util/basic-filebuf.h: void _M_write_mode(); util/basic-filebuf.h:void util/basic-filebuf.h:void util/basic-filebuf.h: reinterpret_castfrom darknet/src/blas.c, gemm.c, matrix.c, utils.c
```c void reorg_cpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out) void flatten(float *x, int size, int layers, int batch, int forward) void weighted_sum_cpu(float *a, float *b, float *s, int n, float *c) void weighted_delta_cpu(float *a, float *b, float *s, float *da, float *db, float *ds, int n, float *dc) void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out) void mean_cpu(float *x, int batch, int filters, int spatial, float *mean) void variance_cpu(float *x, float *mean, int batch, int filters, int spatial, float *variance) void l2normalize_cpu(float *x, float *dx, int batch, int filters, int spatial) void normalize_cpu(float *x, float *mean, float *variance, int batch, int filters, int spatial) void const_cpu(int N, float ALPHA, float *X, int INCX) void mul_cpu(int N, float *X, int INCX, float *Y, int INCY) void pow_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY) void axpy_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY) void scal_cpu(int N, float ALPHA, float *X, int INCX) void fill_cpu(int N, float ALPHA, float *X, int INCX) void deinter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT) void inter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT) void copy_cpu(int N, float *X, int INCX, float *Y, int INCY) void mult_add_into_cpu(int N, float *X, float *Y, float *Z) void smooth_l1_cpu(int n, float *pred, float *truth, float *delta, float *error) void l1_cpu(int n, float *pred, float *truth, float *delta, float *error) void softmax_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error) void logistic_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error) void l2_cpu(int n, float *pred, float *truth, float *delta, float *error) void softmax(float *input, int n, float temp, int stride, float *output) void softmax_cpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output) void upsample_cpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out) void gemm_bin(int M, int N, int K, float ALPHA, void time_random_matrix(int TA, int TB, int m, int k, int n) void gemm(int TA, int TB, int M, int N, int K, float ALPHA, void gemm_nn(int M, int N, int K, float ALPHA, void gemm_nt(int M, int N, int K, float ALPHA, void gemm_tn(int M, int N, int K, float ALPHA, void gemm_tt(int M, int N, int K, float ALPHA, void gemm_cpu(int TA, int TB, int M, int N, int K, float ALPHA, void gemm_gpu(int TA, int TB, int M, int N, int K, float ALPHA, void time_gpu_random_matrix(int TA, int TB, int m, int k, int n) void time_gpu(int TA, int TB, int m, int k, int n) void test_gpu_accuracy(int TA, int TB, int m, int k, int n) void cuda_set_device(int n) void check_error(cudaError_t status) cudaError_t status = cudaMalloc((void **)&x_gpu, size); void cuda_random(float *x_gpu, size_t n) cudaError_t status = cudaMalloc((void **)&x_gpu, size); void cuda_free(float *x_gpu) void cuda_push_array(float *x_gpu, float *x, size_t n) void cuda_pull_array(float *x_gpu, float *x, size_t n) void cuda_set_device(int n){} void free_matrix(matrix m) void scale_matrix(matrix m, float scale) void matrix_add_matrix(matrix from, matrix to) void matrix_to_csv(matrix m) void print_matrix(matrix m) void sorta_shuffle(void *arr, size_t n, size_t size, size_t sections) void shuffle(void *arr, size_t n, size_t size) void *swp = calloc(1, size); void del_arg(int argc, char **argv, int index) void pm(int M, int N, float *A) void find_replace(char *str, char *orig, char *rep, char *output) void top_k(float *a, int n, int k, int *index) void error(const char *s) void malloc_error() void file_error(char *s) void strip(char *s) void strip_char(char *s, char bad) void free_ptrs(void **ptrs, int n) void write_int(int fd, int n) void read_all(int fd, char *buffer, size_t bytes) void write_all(int fd, char *buffer, size_t bytes) void mean_arrays(float **a, int n, int els, float *avg) void print_statistics(float *a, int n) void normalize_array(float *a, int n) void translate_array(float *a, int n, float s) void scale_array(float *a, int n, float s) ```google fast approx fftw