/*	GPL3+	*/
/* Copyright (C) 2019 Momi-g
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/*-*
@name	dq
@auther momi-g
@brief	auto growing array
@synopsys
	// dflfuncs idq/ddq/pdq corresponds to int/double/void* payload   
	#include "dq.h"

	dq_t* idq_new(void)
	void idq_free(dq_t* dq)
	void idq_frees(dq_t* dq)
	
	void idq_push(dq_t* dq, int i)		// void pdq_push(dq_t* dq, void* p)
	int idq_pop(dq_t* dq)				// double ddq_pop(dq_t* dq)
	void idq_lift(dq_t* dq, int i)
	int idq_drop(dq_t* dq)

	int idq_popn(dq_t* dq, int cnt)
	int idq_dropn(dq_t* dq, int cnt)
	void idq_reset(dq_t* dq)
	
	int idq_rep(dq_t* dq, int idx, int i)
	int idq_see(dq_t* dq, int idx)
	int* idq_2arr(dq_t* dq)
	
	// custom macros
	#define DQ_CMN		deq		// >> ideq_new()
	DQ_IMPL([scope], [type], [pfix])	// DQ_IMPL(static, int32_t, i32)
	DQ_VOIDCALL()	// stop compile nouse warning

@eg
 // lift >>                              << push
 //         | head[0] | ... | tail[-1] |
 // drop <<                              >> pop
 
 #include "dq.h"
 int main(int argc, char** argv) {
 	int v;
 	dq_t* q=idq_new();	//		|NULL|
 
 	idq_push(q,11);		//		| 11 |
 	idq_push(q,22);		//		| 11 | 22 |
 	idq_push(q,33);		//		| 11 | 22 | 33 |
 	v = idq_pop(q);		//		| 11 | 22 |		>> v=33
 
 	idq_lift(q,10);		//		| 10 | 11 | 22 |
 	idq_lift(q,20);		//		| 20 | 10 | 11 | 22 |
 	v = idq_drop(q);	//		| 10 | 11 | 22 |	>> v=20
 
 	v = idq_see(q,0);	//		| 10 | 11 | 22 |	>> v=10
 	v = idq_see(q,-1);	//		| 10 | 11 | 22 |	>> v=22 (-1: tail)
 	v = idq_len(q);		//		| 10 | 11 | 22 |	>> v=3

 	idq_dropn(q,2);		//		| 22 |		>> drop x2
//	idq_popn(q,2);		//		| 10 |		>> pop x2
//	idq_reset(q);		//		|NULL|		>> clear all
//	idq_dropn(q,-1);	//		|NULL|		>> same as above
 	v = idq_len(q);		//		| 22 |		>> v=1
 	
// conv to arr
	int* arr = idq_2arr(q);	//	dup payload. 
 	dq_free(q);		// arr[0] == 22, duped.
	free(arr);		// needs free() same as c99 strdup()
 
// works only pdq func, 'pdq_frees()'
 	dq_t* dq = pdq_new();
 	void* p = malloc(32);
 	pdq_push(dq, p);
 	pdq_frees(dq);	//... free() payload and destroy dq.
 	// free(p);		...no need.
 	
// use voidcall if you detect unused warning at compile
	// DQ_VOIDCALL();
	return 0;
 }
 // ~$ gcc src.c dq.c

@param dq	arr/deque obj. holds memsz, idxdata, arrlen etc.
@param idx	tgt index. start with 0 and tail is -1. allows, -2, -3 etc.
@param cnt	popn/dropn takes cnt. cnt== -1 works as dq_reset().
@return	?	val/len etc. rtntype depends on new() type. popn/dropn rtns lastone.
@details
 - other type payload
	dflapi is only idq/ddq/pdq. you can use DQ_IMPL() macro for your own types.
		#include "dq.h"

		typedef struct my_tag{int a, int b} my_t;
		DQ_IMPL(static, my_t, zz)
		//	...>> expand macro
		//	static dq_t* zzdq_new(void){ ..code.. }
		//	static void zzdq_push(dq_t* dq, my_t v){..code..}
		//	static my_t* zzdq_2arr(dq_t* dq){ ..code.. }
		//.. new/push/pop(n)/lift/drop(n)/len/see/rep/reset/2arr/free
		dq_t* q = zzdq_new();
		my_t v = {1,2};
		zzdq_push(q, v);
		...

 - use no prefix api 'dq_XXX()'
	dflname 'dq_XXX' is undefined. you can set dfl apiname as follows. 
		#include "dq.h"
		DQ_IMPL(static, void*, )		// 3rd arg is blank

	 	dq_t* q=pdq_new();
		dq_t* Q= dq_new();	// use dq_new() as pdq_new()

 - rename 'dq' word	
	dq() uses cmnname 'dq' in func and type, idq_new()/dq_t.
	you can change word if set macro 'DQ_CMN' before includes dq.h 
	
		#define DQ_CMN	stack
		#include "dq.h"
		...
		stack_t* stk = istack_new();		// dq_t* stk = idq_new();	...dfl
		istack_push(stk, 10);
		...
	--
		#define DQ_CMN	stk
		#include "dq.h"
		typedef struct my_tag{int a; int b;} my_t;
		DQ_IMPL(static, int, )
		DQ_IMPL(static, my_t, m)
		
		int main(int argc, char** argv){
			stk_t* stk = stk_new();
			stk_push(stk, 10);
			stk_free(stk);
		
			mstk_t* obj = mstk_new();
			mstk_push(obj, (my_t){11,22} );		// (my_t)..compound lit, c99+
			mstk_free(obj);
			return 0;
		}

 - stop compile warning
	compile warning 'unused' will raise if you doesnt use idq()/ddq()/pdq().
	you can stop warning by calling DQ_VOIDCALL(). 

		#include "dq.h"
		int main(int argc, char** argv){
			DQ_VOIDCALL();
			return 0;
		}

	..DQ_VOIDCALL() do nothing. funcs are described in the unreachable block.

@_note
	-- sloppy speedtest( -O0)
	 - read/write
	arr[2]	:	36-41 ms
	dq_rep():	560-600 ms	(15-20 times slow, -O2:250ms, -O2+inline: 190ms)
	dq_2arr():	38-41 ms
	 - write
	arr[2]	:	48-50 ms
	dq_push():	300-350 ms	(6-8 times slow)
	
	 ..overhead src: idx funccall / idx calc+errck
	 ..'inline' will not be needed in generally
	
	-- test code
	int sz= 10 * 1000*1000;
	 - read/write
	for(int i=0; i<sz;i++){ arr[0]=arr[1]+1;}	//36-41ms
	for(int i=0; i<sz;i++){ idq_rep(dq, -1, idq_see(dq,0)+1);}	//370-390ms
	for(int i=0; i<sz;i++){ d2arr[1]=d2arr[0]+1; }	//38-41ms
	
	 - write
	for(i=0; i<sz-1;i++){ arr[0]=i%10;}	//48-50ms
	for(i=0; i<sz-1;i++){ idq_push(dq, i%10); }	//300-350ms

	- inline
	DQ_IMPL(static inline, int, ii)
	for(int i=0; i<sz;i++){ iidq_rep(dq, -1, iidq_see(dq,0)+1);}	//190ms
	
@conforming c89+
@version 2021-07-10 v1.1.2
-*/
#include "dq.h"	/*SH_co* -std=c89	*/
#include <stdio.h>

/* common_funcs */
size_t DQ_SCAT2(DQ_BNAME,_len)(DQ_BTYPE* dq) { return dq->len; DQ_VOIDCALL();}
void DQ_SCAT2(DQ_BNAME,_free)(DQ_BTYPE* dq) {
	if(dq==NULL) {return;}
	memset(dq->arr, '\0', dq->tpsz * dq->mlen);
	free(dq->arr);
	memset(dq, '\0', sizeof(DQ_BTYPE) );
	free(dq);
}
void DQ_SCAT2(DQ_BNAME,_frees)(DQ_BTYPE* dq) {
	int pos, i;
	void** arr;
	if(dq==NULL) {return;}
	DQ_PANIC(dq->tp[0] != 'p', "dq_frees() only works for ptr payload", exit(1) );
	pos = dq->sidx+1;
	arr = (void**)dq->arr;
	i=0;
	for(i=0; i<dq->len; i++) {
		if(dq->mlen <= pos){pos=0;}
		free(arr[pos]);
		pos++;
	}
	DQ_SCAT2(DQ_BNAME,_free)(dq); 
}
void DQ_SCAT2(DQ_BNAME,_extend)(DQ_BTYPE* dq) {
	char* newbuf;
	int cnt;
	newbuf = (char*)realloc(dq->arr, dq->mlen*2 * dq->tpsz );
	DQ_PANIC(newbuf==NULL, "realloc() failed.", exit(1) );
	cnt = dq->mlen - dq->eidx;
/*
//int* i=(int*)newbuf;
//printf("%d\n", *i );
// for(int i=0;i<32;i++){ printf("%d ", ((int*)dq->arr)[i] ); }
// printf("\n%d %d %d\n", dq->sidx, dq->eidx, cnt );

//倍だから絶対に重ならない. 足せばぴったりsidxは移動する
//sスライドかeスライドかの違い。eスライドの方がわずかに早いか。
//一度でもdropしたらsの方が早い。sの方がわかりやすいしs採用
*/
	memcpy( newbuf + (dq->eidx+dq->mlen)*dq->tpsz
		, newbuf + (dq->eidx)*dq->tpsz
		, cnt * dq->tpsz);
/*printf("%d\n", *i );//*/
	dq->arr = newbuf;
	dq->sidx = dq->eidx+dq->mlen -1;
	dq->mlen *= 2;
/*printf("\n%d %d %d\n", dq->sidx, dq->eidx, cnt ); //*/
/*printf("%d\n", dq->sidx); //*/
}
int  DQ_SCAT2(DQ_BNAME,_getidx)(DQ_BTYPE* dq, int idx){
	int bk=idx;
	if(idx<0){idx+=dq->len;}
	DQ_PANIC(idx<0||dq->len<=idx, "bad idx req"
		, printf("dq_len:%d, req:%d\n", dq->len, bk); exit(1) );
	idx += dq->sidx+1;
	if(idx >= dq->mlen){ idx -= dq->mlen; }
	return idx;
}
/* 1st is scope, extern/static */
DQ_IMPLsub(extern ,int		, i, DQ_DFLLEN, DQ_BNAME, DQ_BTYPE, DQ_BNAME, DQ_BTYPE)
DQ_IMPLsub(extern ,double	, d, DQ_DFLLEN, DQ_BNAME, DQ_BTYPE, DQ_BNAME, DQ_BTYPE)
DQ_IMPLsub(extern ,void*	, p, DQ_DFLLEN, DQ_BNAME, DQ_BTYPE, DQ_BNAME, DQ_BTYPE)












/*SH_SMP
#include <stdio.h>
#include "dq.h"

typedef struct my_tag{ int a; int b;} my_t;
DQ_IMPL(static, my_t, my);

int main(int argc, char** argv) {
	DQ_VOIDCALL();
	puts("hw");
	dq_t* dq = mydq_new();
	my_t obj = {1,2};
	mydq_push(dq, obj);
printf("%d\n", mydq_len(dq) );
	obj = mydq_pop(dq);
printf("%d\n", obj.b );
	mydq_free(dq);
	return 0;
}
// ~$ gcc src.c libdq.a
//SH_SMPE*/

/*
 change log
 --
2021-07-10  Momi-g	<dmy@dmy.dmy>

	* dq(dq_2arr): fix len==1 b/o write err. add len==1

2021-05-20  Momi-g	<dmy@dmy.dmy>

	* dq(all): fix -Wpedantic warning, improve sh.c macros

	* dq(test): add -O0, -O2 test, fixdoc

2021-03-27  Momi-g	<dmy@dmy.dmy>

	* dq(all): add NULL obj PANIC()

2021-02-23  Momi-g	<dmy@dmy.dmy>

	* dq(_2arr): fix invalid memcpy() range.

2021-02-22  Momi-g	<dmy@dmy.dmy>

	* dq(all): change api. v1.1.0
	* (settail/sethead): omit. replace to popn(), dropn()
	* (pop/drop): change api. pop/drop() takes only 1 args
	* (popn/dropn): new api. popn/dropn() takes 2 args for multi remove

2021-02-19  Momi-g	<dmy@dmy.dmy>

	* dq.c(dq_reset): add newfunc. fix macros
	
	* dq.c(all): change dq_obj struct for use lowcost i++. 50ms -> 40ms

2021-01-30  Momi-g	<dmy@dmy.dmy>

	* dq.c(DQ_VOIDCALL): add new macro for kill compile warning, dmycall.
	(DQ_CMN): add
	(DQ_LINKBASE): fix
	(DQ_IMPLsub): fix new(), add voidfunccall()

2021-01-21  Momi-g	<dmy@dmy.dmy>

	* dq.c: v1.0.0

*/

