/* Copyright (C) 2021 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	charbits
@auther momi-g
@brief	macros, bitcalc with char/int/any arr[]
@_synopsys
	#define CBIT_NOT(p, sz)		
	#define CBIT_RR(p, sz, sft) 
	#define CBIT_LL(p, sz, sft) 
								
	#define CBIT_OR(p1, p2, sz)
	#define CBIT_AND(p1, p2, sz)
	#define CBIT_XOR(p1, p2, sz)
	#define CBIT_SET(p1, p2, sz)	//not bitop. assing arr, p1=p2
	// all macros can set a caststr to ag1, XXX( (float),p1,p2,sz) etc.  

@_eg
	#include "charbits.hpp"
	#include <stdio.h>

	int main(int argc, char** argv){
		char p1[] = {1,0,0,0, 0,0,0,0};		// {1000 0000}
		char p2[] = {1,0,1,0, 0,0,0,0,1};	// {1010 0000 1}
		
		CBIT_NOT(p1, 8);	// {0111 1111}
		CBIT_LL(p1, 8, 2);	// {1111 1100}	// arr shift. drop p1[0], p1[1]
		CBIT_RR(p1, 6, 3);	// {0001 1100}	// handle p1[0]-p1[5], sz==6
		
		CBIT_OR( (char), p1, p2, 8);	// {1011 1101},  p1[n]=(char)p2[n]
		CBIT_AND(p1, p2, 8);// {1010 0000}
		
		CBIT_NOT(p1, 2);	// {0110 0000}	ignore p1[2]-
		// CBIT_NOT(p1, 100);	// sizeover. cause SIGSEGV
		
		for(int i=0;i<8;i++){printf("%d ",p1[i]);}
		puts("");
		return 0;
	}
	// ~$ ~$ gcc -E -P eg.c
	// ~$ ~$ gcc eg.c

@param	p	arrhead ptr. int arr[10]={0}; char* arr = malloc(12); etc.
@param	sz	idx limit for calc.	 0000 >>> CBIT_NOT(p,2) >>> 1100 
@param	sft	RR/LL needs shift size. size is unlimited until size_t max.
@details
 - macros treats val as bool but try to hold rawdata.
   priority is ag1>ag2.  macros changes rawdata to 0/1 if invert bit/byte.
		int p1[] = {10,0,12,0};	// 1010
		int p2[] = {20,0,0,23};	// 1001
		CBIT_OR(p1, p2, 4);	//  {10,0,12,23}
		CBIT_NOT(p1);	// {0,1,0,0}
	
 - idxlimit setting must be smaller than p1 and p2 size.
		float p1[] = {1,1,1,0};
		float p2[] = {1,0,0,0};
		CBIT_AND(p1, p2, 16);	// NG. out of idx range
		CBIT_AND(p1, p2, 2);	// ok. get {1,0,1,0}. check only first 2 idx.

 - shift direction 
   someone feels the shift operation is reversed. set your macro if necessary. 
		#include "charbits.hpp"
		#define CBIT_myRR	CBIT_LL
		#define CBIT_myLL	CBIT_RR
		int main(int argc, char** argv){ ...

 - cast option
   macros can handle different types of arrays.
		#include "charbits.hpp"
		int main(int argc, char** argv){
			char p1[] = {1,0,1,0};
			void* p2[] ={0,1,0,0};
			CBIT_OR(p1, p2, 4); //{1110}	val is cast to void* >> char
			return 0;
		}

	but some oparation causes type compatibility warning, int a=(float)3 etc.
	you can stop warning by setting caststr to macro arg1.
			char p1[4];
			void* p2[8];

			CBIT_XOR(        p1, p2, 4);	// p1[n]= p2[n]
			CBIT_XOR((char), p1, p2, 4);	// p1[n]= (char)p2[n]
			CBIT_OR((void*), p2, p1, 4);	// p2[n]= (void*)p1[n]
			CBIT_NOT((void*)(int),p2,6);	// p2[n]= (void*)(int)1  etc
	
	see ~$ gcc -E -P eg.c
	
@_note
https://programming-place.net/ppp/contents/c/rev_res/array011.html#way1
https://stackoverflow.com/questions/2632300	Q: Ed Marty  ->  A: Matthew Slattery
@conforming posix-2001+
@version 1.0.0, 2021-02-12
-*/
#include <features.h>
#if ( _POSIX_C_SOURCE +0 < 200112L )
	#include "needs compiler posix-2001 or upper(c99+)"
#endif

/* api */
#define CBIT_NOT	CHARBITS_NOT
#define CBIT_RR     CHARBITS_RR
#define CBIT_LL     CHARBITS_LL
#define CBIT_OR     CHARBITS_OR
#define CBIT_AND    CHARBITS_AND
#define CBIT_XOR    CHARBITS_XOR
#define CBIT_SET    CHARBITS_SET

/* impl */
#include <string.h>

// from license: cc-by-sa 2.5 (code is changed from the orig)
// https://stackoverflow.com/questions/2632300	Q: Ed Marty  ->  A: Matthew Slattery
#define CHARBITS_CAST(...) CHARBITS_CASTsub(__VA_ARGS__, CHARBITS_CASTset, CHARBITS_CASTdfl)
#define CHARBITS_CASTsub(a1,a2,a3,a4,mc,...)	mc(a1)
#define CHARBITS_CASTset(a1)	a1
#define CHARBITS_CASTdfl(a1)	

#define CHARBITS_CNT(...) CHARBITS_CNTsub(__VA_ARGS__, CHARBITS_C4, CHARBITS_C3)
#define CHARBITS_CNTsub(a1,a2,a3,a4,mc,...)	mc(a1,a2,a3,a4)
#define CHARBITS_C4(a1,a2,a3,a4)	a2,a3,a4
#define CHARBITS_C3(a1,a2,a3,a4)	a1,a2,a3	
// end licence:	cc-by-sa 2.5

#define CHARBITS_NOT(...) CHARBITS_impl(NOT, CHARBITS_CAST(__VA_ARGS__, dmy), CHARBITS_CNT(__VA_ARGS__,dmy) )
#define  CHARBITS_RR(...) CHARBITS_impl( RR, CHARBITS_CAST(__VA_ARGS__     ), CHARBITS_CNT(__VA_ARGS__    ) )
#define  CHARBITS_LL(...) CHARBITS_impl( LL, CHARBITS_CAST(__VA_ARGS__     ), CHARBITS_CNT(__VA_ARGS__    ) )
#define  CHARBITS_OR(...) CHARBITS_impl( OR, CHARBITS_CAST(__VA_ARGS__     ), CHARBITS_CNT(__VA_ARGS__    ) )
#define CHARBITS_AND(...) CHARBITS_impl(AND, CHARBITS_CAST(__VA_ARGS__     ), CHARBITS_CNT(__VA_ARGS__    ) )
#define CHARBITS_XOR(...) CHARBITS_impl(XOR, CHARBITS_CAST(__VA_ARGS__     ), CHARBITS_CNT(__VA_ARGS__    ) )
#define CHARBITS_SET(...) CHARBITS_impl(SET, CHARBITS_CAST(__VA_ARGS__     ), CHARBITS_CNT(__VA_ARGS__    ) )

#define CHARBITS_impl(suf, ...) CHARBITS_##suf##impl(__VA_ARGS__)
#define CHARBITS_NOTimpl(pre, p, sz, dmy) do{size_t i=0;for(;i<sz;i++){p[i]= pre!p[i];} }while(0)
#define  CHARBITS_RRimpl(pre, p, sz, sft) do{memmove(p+sft,p,sizeof(p[0])*(sz-sft));memset(p,0,sizeof(p[0])*sft);} while(0)
#define  CHARBITS_LLimpl(pre, p, sz, sft) do{memmove(p,p+sft,sizeof(p[0])*(sz-sft));memset(p+sz-sft,0,sizeof(p[0])*sft); }while(0)
#define  CHARBITS_ORimpl(pre, p1, p2, sz) do{size_t i=0;for(;i<sz;i++){p1[i]= (p1[i]||p2[i]) ? (p1[i]?p1[i]:pre p2[i]) : 0;} } while(0)
#define CHARBITS_ANDimpl(pre, p1, p2, sz) do{size_t i=0;for(;i<sz;i++){p1[i]= (p1[i]&&p2[i]) ? p1[i] : 0; } } while(0)
#define CHARBITS_XORimpl(pre, p1, p2, sz) do{size_t i=0;for(;i<sz;i++){p1[i]= (p1[i]&&!p2[i]) ? p1[i]: (!p1[i]&&p2[i])?pre p2[i] : 0; } }while(0)
#define CHARBITS_SETimpl(pre, p1, p2, sz) do{size_t i=0;for(;i<sz;i++){p1[i]= pre p2[i];} } while(0)
