Related
I want to reduce memory storage of chars using Bitwise Operations,
for Example
input: {"ACGT"}
output: {"0xE4"}
where we represtnt the number in binary then into Hexadecimal
if A=00, C=01, G=10, T=11
so ACGT = 0xE4 = 11100100b
I cant Figure the whole way so here is what I did So Far
enum NucleicAc {
A = 0,
C = 1,
G = 2,
T = 3,
} ;
struct _DNA {
char * seq ;
};
DNAString DSCreate(char * mseq) {
DNAString dna = malloc(sizeof(struct _DNA));
if (!dna){
exit(-1);
}
const int length = strlen(mseq);
// left shift will create the base , in the case of ACGT --> 0000,0000
int base = 0 << length * 2;
//loop and set bits
int i = 0 ;
int k = 0 ; // the counter where i want to modify the current bit
//simple looping gor the string
for ( ; i < length ; i++ ) {
switch (*(mseq+i)) {
case 'A': // 00
k++;
break;
case 'C': // 0 1
modifyBit(&base, k, 1);
k++;
break;
case 'G': //10
k++;
modifyBit(&base,k , 1);
break;
case 'T': // 11
modifyBit(&base, k, 1);
k++;
modifyBit(&base, k,1);
break;
default:
break;
} //end of switch
k++;
}//end of for
char * generatedSeq ;
//convert the base to hex ??
return dna;
}
void bin(unsigned n){
unsigned i;
for (i = 1 << 7; i > 0; i = i / 2){
(n & i) ? printf("1") : printf("0");
}
}
and if we print the base, the value is 11100100b as expected,
how to store the hexadecimal representation as String to the char *mseq in the struct ?
any direction or exact solutions or is there any better approach?
and also later i want to get the letter using only the index for Example
DSGet(dsStruct , '0')--> will return 'A' hence dsStruct contains the "ACGT" before Encode?
There are several ways you can approach encoding the sequence. Your enum is fine, but for your encoded sequence, a struct that captures the bytes as unsigned char, the original sequence length and the encoded size in bytes will allow an easy decoding. You will get 4-to-1 compression (plus 1 byte if you sequence isn't evenly divisible by 4). The enum and struct could be:
enum { A, C, G, T };
typedef struct {
unsigned char *seq;
size_t len, size;
} encoded;
To map characters in your string to the encoded values a simple function that returns the enum value matching the character is all you need (don't forget to handle any errors)
/* convert character to encoded value */
unsigned char getencval (const char c)
{
if (c == 'A')
return A;
else if (c == 'C')
return C;
else if (c == 'G')
return G;
else if (c == 'T')
return T;
/* exit on anything other than A, C, G, T */
fprintf (stderr, "error: invalid sequence character '%c'\n", c);
exit (EXIT_FAILURE);
}
To encode the original sequence, you will populate the encoded struct with the original length (len) and number of bytes (size) needed to hold the encoded string. Don't forget that any 1-character of the next 4-characters will require another byte of storage. You can use a simple add-and-divide to account for any partial 4-character ending portion of the sequence, e.g.
/* encode sequence of characters as 2-bit pairs (4-characters per-byte)
* returns encoded struct with allocated .seq member, on failure the .seq
* member is NULL. User is resposible for freeing .seq member when done.
*/
encoded encode_seq (const char *seq)
{
size_t len = strlen(seq),
size = (len + 3) / 4; /* integer division intentional */
encoded enc = { .seq = calloc (1, size), /* encoded sequence struct */
.len = len,
.size = size };
if (!enc.seq) { /* validate allication */
perror ("calloc-enc.seq");
return enc;
}
/* loop over each char (i) with byte index (ndx)
* shifting each 2-bit pair by (shift * 2) amount.
*/
for (int i = 0, ndx = 0, shift = 4; seq[i] && seq[i] != '\n'; i++) {
if (!shift--) /* decrement shift, reset if 0 */
shift = 3;
if (i && i % 4 == 0) /* after each 4th char, increment ndx */
ndx += 1;
/* shift each encoded value (multiply by 2 for shift of 6, 4, 2, 0) */
enc.seq[ndx] |= getencval (seq[i]) << shift * 2;
}
return enc; /* return encoded struct with allocated .seq member */
}
To get the original sequence back from your encoded struct, the use of a lookup table (shown below with the full code) makes it a breeze. You simply loop over all stored byte values appending the corresponding strings from the lookup table until the final byte. For the final byte, you need to determine if it is a partial string and, if so, how many characters remain to copy. (that's why you store the original sequence length in your struct). Then simply use strncat to append that many characters from the final byte, e.g.
/* decodes encoded sequence. Allocates storage for decoded sequence
* and loops over each encoded byte using lookup-table to obtain
* original 4-character string from byte value. User is responsible
* for freeing returned string when done. Returns NULL on allocation
* failure.
*/
char *decode_seq (encoded *eseq)
{
char *seq = malloc (eseq->len + 1); /* allocate storage for sequence */
size_t i = 0, offset = 0, remain;
if (!seq) { /* validate allocation */
perror ("malloc-seq");
return NULL;
}
/* loop appending strings from lookup table for all but last byte */
for (; i < eseq->size - 1; i++) {
memcpy (seq + offset, lookup[eseq->seq[i]], 4);
offset += 4; /* increment offset by 4 */
}
/* determine the number of characters in last byte */
remain = eseq->len - (eseq->size - 1) * 4;
memcpy (seq + offset, lookup[eseq->seq[i]], remain);
seq[offset + remain] = 0; /* nul-terminate seq */
return seq; /* return allocated sequence */
}
Adding the lookup table and putting all the pieces together, one way to approach this problem is:
(edit: lookup table reordered to match your byte-value encoding, optimized decode_seq() to not scan for end-of-string on copy)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { A, C, G, T };
typedef struct {
unsigned char *seq;
size_t len, size;
} encoded;
const char lookup[][5] = {
"AAAA","CAAA","GAAA","TAAA","ACAA","CCAA","GCAA","TCAA",
"AGAA","CGAA","GGAA","TGAA","ATAA","CTAA","GTAA","TTAA",
"AACA","CACA","GACA","TACA","ACCA","CCCA","GCCA","TCCA",
"AGCA","CGCA","GGCA","TGCA","ATCA","CTCA","GTCA","TTCA",
"AAGA","CAGA","GAGA","TAGA","ACGA","CCGA","GCGA","TCGA",
"AGGA","CGGA","GGGA","TGGA","ATGA","CTGA","GTGA","TTGA",
"AATA","CATA","GATA","TATA","ACTA","CCTA","GCTA","TCTA",
"AGTA","CGTA","GGTA","TGTA","ATTA","CTTA","GTTA","TTTA",
"AAAC","CAAC","GAAC","TAAC","ACAC","CCAC","GCAC","TCAC",
"AGAC","CGAC","GGAC","TGAC","ATAC","CTAC","GTAC","TTAC",
"AACC","CACC","GACC","TACC","ACCC","CCCC","GCCC","TCCC",
"AGCC","CGCC","GGCC","TGCC","ATCC","CTCC","GTCC","TTCC",
"AAGC","CAGC","GAGC","TAGC","ACGC","CCGC","GCGC","TCGC",
"AGGC","CGGC","GGGC","TGGC","ATGC","CTGC","GTGC","TTGC",
"AATC","CATC","GATC","TATC","ACTC","CCTC","GCTC","TCTC",
"AGTC","CGTC","GGTC","TGTC","ATTC","CTTC","GTTC","TTTC",
"AAAG","CAAG","GAAG","TAAG","ACAG","CCAG","GCAG","TCAG",
"AGAG","CGAG","GGAG","TGAG","ATAG","CTAG","GTAG","TTAG",
"AACG","CACG","GACG","TACG","ACCG","CCCG","GCCG","TCCG",
"AGCG","CGCG","GGCG","TGCG","ATCG","CTCG","GTCG","TTCG",
"AAGG","CAGG","GAGG","TAGG","ACGG","CCGG","GCGG","TCGG",
"AGGG","CGGG","GGGG","TGGG","ATGG","CTGG","GTGG","TTGG",
"AATG","CATG","GATG","TATG","ACTG","CCTG","GCTG","TCTG",
"AGTG","CGTG","GGTG","TGTG","ATTG","CTTG","GTTG","TTTG",
"AAAT","CAAT","GAAT","TAAT","ACAT","CCAT","GCAT","TCAT",
"AGAT","CGAT","GGAT","TGAT","ATAT","CTAT","GTAT","TTAT",
"AACT","CACT","GACT","TACT","ACCT","CCCT","GCCT","TCCT",
"AGCT","CGCT","GGCT","TGCT","ATCT","CTCT","GTCT","TTCT",
"AAGT","CAGT","GAGT","TAGT","ACGT","CCGT","GCGT","TCGT",
"AGGT","CGGT","GGGT","TGGT","ATGT","CTGT","GTGT","TTGT",
"AATT","CATT","GATT","TATT","ACTT","CCTT","GCTT","TCTT",
"AGTT","CGTT","GGTT","TGTT","ATTT","CTTT","GTTT","TTTT"};
/* convert character to encoded value */
unsigned char getencval (const char c)
{
if (c == 'A')
return A;
else if (c == 'C')
return C;
else if (c == 'G')
return G;
else if (c == 'T')
return T;
/* exit on anything other than A, C, G, T */
fprintf (stderr, "error: invalid sequence character '%c'\n", c);
exit (EXIT_FAILURE);
}
/* encode sequence of characters as 2-bit pairs (4-characters per-byte)
* returns encoded struct with allocated .seq member, on failure the .seq
* member is NULL. User is resposible for freeing .seq member when done.
*/
encoded encode_seq (const char *seq)
{
size_t len = strlen(seq),
size = (len + 3) / 4; /* integer division intentional */
encoded enc = { .seq = calloc (1, size), /* encoded sequence struct */
.len = len,
.size = size };
if (!enc.seq) { /* validate allication */
perror ("calloc-enc.seq");
return enc;
}
/* loop over each char (i) with byte index (ndx)
* shifting each 2-bit pair by (shift * 2) amount.
*/
for (int i = 0, ndx = 0, shift = 0; seq[i] && seq[i] != '\n'; i++, shift++) {
if (shift == 4) /* reset to 0 */
shift = 0;
if (i && i % 4 == 0) /* after each 4th char, increment ndx */
ndx += 1;
/* shift each encoded value (multiply by 2 for shift of 0, 2, 4, 6) */
enc.seq[ndx] |= getencval (seq[i]) << shift * 2;
}
return enc; /* return encoded struct with allocated .seq member */
}
/* decodes encoded sequence. Allocates storage for decoded sequence
* and loops over each encoded byte using lookup-table to obtain
* original 4-character string from byte value. User is responsible
* for freeing returned string when done. Returns NULL on allocation
* failure.
*/
char *decode_seq (encoded *eseq)
{
char *seq = malloc (eseq->len + 1); /* allocate storage for sequence */
size_t i = 0, offset = 0, remain;
if (!seq) { /* validate allocation */
perror ("malloc-seq");
return NULL;
}
/* loop appending strings from lookup table for all but last byte */
for (; i < eseq->size - 1; i++) {
memcpy (seq + offset, lookup[eseq->seq[i]], 4);
offset += 4; /* increment offset by 4 */
}
/* determine the number of characters in last byte */
remain = eseq->len - (eseq->size - 1) * 4;
memcpy (seq + offset, lookup[eseq->seq[i]], remain);
seq[offset + remain] = 0; /* nul-terminate seq */
return seq; /* return allocated sequence */
}
/* short example program that takes string to encode as 1st argument
* using "ACGT" if no argument is provided by default
*/
int main (int argc, char **argv) {
char *seq = NULL;
encoded enc = encode_seq(argc > 1 ? argv[1] : "ACGT");
if (!enc.seq) /* validate encoded allocation */
return 1;
/* output original string, length and encoded size */
printf ("encoded str : %s\nencoded len : %zu\nencoded size : %zu\n",
argc > 1 ? argv[1] : "ACGT", enc.len, enc.size);
/* loop outputting byte-values of encoded string */
fputs ("encoded seq :", stdout);
for (size_t i = 0; i < enc.size; i++)
printf (" 0x%02x", enc.seq[i]);
putchar ('\n');
seq = decode_seq (&enc); /* decode seq from byte values */
printf ("decoded seq : %s\n", seq); /* output decoded string */
free (seq); /* don't forget to free what you allocated */
free (enc.seq);
}
In most cases a lookup-table provides a great deal of efficiency advantage compared to computing and building each 4-character string during decoding. This is enhanced by the lookup table staying resident in cache for most cases.
The length of the DNA sequence you can encode and decode is limited only by the amount of virtual memory you have available.
Example Use/Output
The program takes the sequence to encode and decode as the first argument (default "ACGT"). So the default output is:
$ ./bin/dnaencodedecode
encoded str : ACGT
encoded len : 4
encoded size : 1
encoded seq : 0xe4
decoded seq : ACGT
4-byte encoded in 1-byte. Note the byte value of 0x1b and not 0xe4 due to the table ordering.
A longer example:
./bin/dnaencodedecode ACGTGGGTCAGACTTA
encoded str : ACGTGGGTCAGACTTA
encoded len : 16
encoded size : 4
encoded seq : 0xe4 0xea 0x21 0x3d
decoded seq : ACGTGGGTCAGACTTA
16-character encoded in 4-bytes.
Finally, what of a sequence that isn't divisible by 4 so you have a partial number of characters in the last encoded byte? That is handled as well, e.g.
$ ./bin/dnaencodedecode ACGTGGGTCAGACTTAG
encoded str : ACGTGGGTCAGACTTAG
encoded len : 17
encoded size : 5
encoded seq : 0xe4 0xea 0x21 0x3d 0x02
decoded seq : ACGTGGGTCAGACTTAG
17 characters encoded in 5-bytes. (not the pure 4-to-1 compression, but as the sequence size increases, the significance of any partial group of characters in the last byte becomes negligible)
As far a perfomance, for a sequence of 100,000 characters and output of the the byte values and strings replaced with a simple loop that compares the decoded seq to the original argv[1] it only takes a few thousandth of a second (on an old i7 Gen2 laptop with SSD) to encode and decode and validate, e.g.
$
time ./bin/dnaencodedecodet2big $(< dat/dnaseq100k.txt)
encoded len : 100000
encoded size : 25000
all tests passed
real 0m0.014s
user 0m0.012s
sys 0m0.003s
There are a lot of ways to do this, but given your description, this was what came to my mind that you were trying to accomplish. There is a lot here, so take your time going through it.
Look things over (the code is commented), and let me know if you have further questions. Just drop a comment below.
This should do what you want. I thought the compression was a pretty cool idea so I wrote this real quick. As mentioned by #kaylum, hex encoding is just a way to read the underlying data in memory, which is always just bits. So, you only need to worry about that on print statements.
Let me know if this works or you have any questions about what I did.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct {
unsigned char *bits;
unsigned long length; // use this to store the number of letters encoded, for safety
} DNA;
typedef enum {
A = 0,
C = 1,
G = 2,
T = 3
} NucleicAc;
This returns the base at a given index with some bounds checking
char base_at_index(DNA *dna, unsigned long index) {
if (index >= dna->length) {
fputs("error: base_at_index: index out of range", stderr);
exit(EXIT_FAILURE);
}
// offset is index / 4, this gives us the correct byte
// shift amount is index % 4 to give us the correct 2 bits within the byte.
// This must then be multiplied by 2 because
// each base takes 2 bits to encode
// then we have to bitwise-and this value with
// 3 (0000 0011 in binary) to retrieve the bits we want.
// so, the formula we need is
// (dna->bits[index / 4] >> (2 * (index % 4))) & 3
switch((dna->bits[index / 4] >> 2 * (index % 4)) & 3) {
case A: return 'A';
case C: return 'C';
case G: return 'G';
case T: return 'T';
default:
fputs("error: base_at_index: invalid encoding", stderr);
exit(EXIT_FAILURE);
}
}
This encodes a string of bases to bytes
/* you can fit four 2-bit DNA codes in each byte (unsigned char).
len is the maximum number of characters to read. result must be at least len bytes long
*/
void encode_dna(unsigned char *result, char *sequence, unsigned long len) {
// keep track of what byte we are on in the result
unsigned result_index = 0;
// our shift for writing to the correct position in the byte
unsigned shift = 0;
// first clear result or else bitwise operations will produce errors
// this could be removed if you were certain result parameter was zero-filled
memset(result, 0, len);
// iterate through characters of the sequence
while(*sequence) {
switch (*sequence) {
// do nothing for 'A' since it is just zero
case 'A': break;
case 'C':
// we are doing a bitwise or with the current byte
// and C (1) shifted to the appropriate position within
// the byte, and then assigning the byte with the result
result[result_index] |= C << shift;
break;
case 'G':
result[result_index] |= G << shift;
break;
case 'T':
result[result_index] |= T << shift;
break;
default:
fputs("error: encode_dna: invalid base pair", stderr);
exit(EXIT_FAILURE);
}
// increase shift amount by 2 to the next 2-bit slot in the byte
shift += 2;
// on every 4th iteration, reset our shift to zero since the byte is now full
// and move to the next byte in our result buffer
if (shift == 8) {
shift = 0;
result_index++;
}
// advance sequence to next nucleotide character
sequence++;
}
}
And here's a test
int main(int argc, char **argv) {
// allocate some storage for encoded DNA
unsigned char encoded_dna[32];
const unsigned long sample_length = 15;
// encode the given sample sequence
encode_dna(encoded_dna, "ACGTAGTCGTCATAG", sample_length);
// hh here means half of half word, which is a byte
// capital X for capitalized hex output
// here we print some bytes
printf("0x%hhX\n", encoded_dna[0]); // should output 0xE4
printf("0x%hhX\n", encoded_dna[1]); // should be 0x78
printf("0x%hhX\n", encoded_dna[2]); // should be 0x1E
printf("0x%hhX\n", encoded_dna[3]); // should be 0x23
DNA test_dna; // allocate a sample DNA structure
test_dna.bits = encoded_dna;
test_dna.length = sample_length; // length of the sample sequence above
// test some indices and see if the results are correct
printf("test_dna index 4: %c\n", base_at_index(&test_dna, 4));
printf("test_dna index 7: %c\n", base_at_index(&test_dna, 7));
printf("test_dna index 12: %c\n", base_at_index(&test_dna, 12));
return 0;
}
Output:
0xE4
0x78
0x1E
0x23
test_dna index 4: A
test_dna index 7: C
test_dna index 12: T
Assuming you really do want to encode your dna string into a hex string and that you want to read the input string from left to right, but output hex chars right to left, here's a simple, but slightly slow implementation.
First, your DNAString needs to keep track whether there are really an even or odd number of acid sequences in the list. This will make additional appendages easier.
struct DNAString
{
char* seq;
bool odd; // if odd bit is set, then the front char is already allocated and holds one acid
};
And now let's introduce a little helper function to convert ACGT into 0,1,2,3.
char acid_to_value(char c)
{
switch (c)
{
case 'A': return 0;
case 'C': return 1;
case 'G': return 2;
case 'T': return 3;
}
// ASSERT(false)
return 0;
}
Then the core implementation is to keep "prepending" new hexchars onto the string your are building. If the string is already of an odd length, the code will just "fixup" the front character by converting it from hex to integer, then shifting the new acid value into it, then converting it back to a hex char
extern char fixup(char previous, char acid);
{
char hexchars[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
char tmp[2] = { previous, '\0' };
unsigned long asnumber = strtol(tmp, nullptr, 16);
asnumber = asnumber & 0x3; // last two bits
asnumber = asnumber | (acid_to_value(acid) << 2);
return hexchars[asnumber];
}
void prepend_nucleic_acid_to_hexstring(struct DNAString* dnastring, char acid)
{
if (dnastring->odd)
{
// find the first char in the string and fix it up hexwise
dnastring->seq[0] = fixup(dnastring->seq[0], acid);
dnastring->odd = false;
}
else
{
size_t currentlength = dnastring->seq ? strlen(dnastring->seq) : 0;
const char* currentstring = dnastring->seq ? dnastring->seq : "";
char* newseq = (char*)calloc(currentlength + 2, sizeof(char)); // +1 for new char and +1 for null char
newseq[0] = acid_to_value(acid) + '0'; // prepend the next hex char
strcpy(newseq + 1, currentstring); // copy the old string into the new string space
free(dnastring->seq);
dnastring->seq = newseq;
dnastring->odd = true;
}
}
Then your DNACreate function is real simple:
struct DNAString DSCreate(const char* mseq)
{
DNAString dnastring = { 0 };
while (*mseq)
{
prepend_nucleic_acid_to_hexstring(&dnastring, *mseq);
mseq++;
}
return dnastring;
}
I don't claim this approach to be efficient since he literally keeps reallocating memory for each char. But it does enable you to have flexability to invoke the prepend function later for additional sequencing.
And then to test it:
int main()
{
struct DNAString dnastring = DSCreate("ACGT");
printf("0x%s\n", dnastring.seq);
return 0;
}
I have a function PostProcess that is fixed and cannot change. It takes an array of 6 bytes and outputs a 24-bit value.
I'm trying to work out that for a given 24-bit number what function PreProcess would give me the same output and input values.
For example if I set my input value to be 2^24 -1 = 16777215 then I would expect to get 16777215 on the output.
It's not clear how I would implement this functionality. I've added the code below with a test and the functionality of PostProcess
void PreProcess(unsigned int in, unsigned char out[]);
int PostProcess(unsigned char pu8Input[]);
int main()
{
unsigned int InputVal = 16777215; // max value for 24 bits
unsigned char PreProcessed[6] = {0};
PreProcess(InputVal,PreProcessed);
unsigned int OutputVal = PostProcess(PreProcessed);
if(InputVal == OutputVal)
printf("True!");
else
printf("False");
return 0;
}
void PreProcess(unsigned int in, unsigned char out[])
{
//TODO
}
int PostProcess(unsigned char pu8Input[])
{
unsigned int u32Out = 0u;
u32Out += (pu8Input[0] - '0') * 100000;
u32Out += (pu8Input[1] - '0') * 10000;
u32Out += (pu8Input[2] - '0') * 1000;
u32Out += (pu8Input[3] - '0') * 100;
u32Out += (pu8Input[4] - '0') * 10;
u32Out += (pu8Input[5] - '0') * 1;
u32Out &= 0xFFFFFF;
return u32Out;
}
Reverse the operation
Note; with in > 999999, out[0] will be outside the '0'-'9' range.
void PreProcess(unsigned int in, unsigned char out[]) {
in &= 0xFFFFFFu; // Enforce 24-bit limit.
for (int index = 5; index > 0; index--) {
out[index] = in%10u + '0';
in /= 10u;
}
// `in` will be 0 to 167
out[0] = in + '0;
// With ASCII, `out[0]` will be 48 to 215
}
The input integer can have the maximum value of 2^24 - 1, the array of characters is 6 bytes long... having the possibility to change PostProcess() it would have been easy: 6 characters are exactly those required to store a 24 bit integer in HEX format. A character every 4 bytes; maximum value (0x) FFFFFF.
But PostProcess() implementation is fixed and it is designed as a sort of "max-6-digits-atoi". So, if the input buffer's value is {'3', '4', '5', '6', '7', '8'}, then the integer 345678.
It seems that 999999 can be printed at most, but here comes the trick: who bounds us to store in the char buffer only digits? We don't have any contraints (but we have to rely on ASCII encoding scheme).
The strategy
Let's make sure that the lest 5 bytes of the char buffer contain the decimal representation of the input number. In this way, the PostProcess will convert those digits as expected. The value of those digits can be calculated as in % 100000
Being the maximum input value 2^24-1 = 16777215, we have to represent the range [0-167] with the first byte of the array
Since the PostProcess will subtract '0' from pu8Input[0], we make sure to compensate it when generating pu8Input[0]
The code
#include <stdio.h>
#include <string.h>
void PreProcess(unsigned int in, unsigned char out[])
{
if(in <= 16777215)
{
char aux[7];
unsigned int auxInt = in % 100000;
unsigned char firstchar;
firstchar = (in / 100000) + '0';
sprintf( aux, "%c%05u", firstchar, auxInt );
memcpy( out, aux, 6 );
}
}
Summarizing:
We calculate the remainder auxInt = in % 100000
We calculate the leading char as firstchar = (in / 100000) + '0'
We put them together with sprintf, using an auxiliary char buffer 7 bytes long (because we need room for the string terminator)
We memcpythe auxiliary char buffer to the output buffer
Short story. I made a program that does addition for binary integers. I need to make it work for binary real numbers (e.g. 1010.1010(binary)=10.625(decimal)
The input is given as a binary string.
I made a lot of attempts and I couldn't find a simple way to do it. Please help create such a program.
Example: {input: 1010.1010(10.625 in decimal) 0.1(0.5 in decimal)
output: 1011.001 (11.125 in decimal)}
Code:
#include <stdio.h>
#include <string.h>
void bin_add(int c[400], int d[400])
{
int car[400]; //carry
int i = 199;
car[i] = 0;
while (i >= 0)
{
//find carry and shift it left
//find the sum
car[i - 1] = (c[i] & d[i]) | (c[i] & car[i]) | (d[i] & car[i]);
c[i] = (c[i] ^ d[i]) ^ car[i];
printf("car[i-1]=%d c[i]=%d\n", car[i - 1], c[i]);
i--;
}
// printf("\n");
}
int main()
{
int l, l1, i;//l and l1 are lengths
char a[200], b[200]; //a and b are the inputs
int c[200], d[200]; //c and d are used for processing
for (i = 0; i < 200; i++)
{
c[i] = 0;
d[i] = 0;
}
gets(a);
gets(b);
l = strlen(a);
l1 = strlen(b);
for (int i = 0; i < l; i++)
{
c[200 - l + i] = a[i] - 48;
}
////////////////////////////////////////////
for (int i = 0; i < l1; i++)
{
d[200 - l1 + i] = b[i] - 48;
}
////////////////////////////////
bin_add(c, d);
for (i = 0; i < 200; i++)
printf("%d", c[i]);
return 0;
}
What you really want to do, is handle each digit in order of increasing importance. To make that easier, you should implement the following functions:
/* Return the number of fractional bits in bs */
int bs_fractbits(const char *bs);
/* Return the number of integer bits in bs */
int bs_intbits(const char *bs);
/* Return the bit in bs corresponding to value 2**i,
0 if outside the bit string */
int bs_bit(const char *bs, int i);
/* Return -1 if bs is negative,
0 if bs is zero or NULL,
+1 if bs is positive */
int bs_sign(const char *bs);
/* Return -1 if bs1 < bs2,
0 if bs1 == bs2,
+1 if bs1 > bs2. */
int bs_cmp(const char *bs1, const char *bs2);
To support negative values, you'll need to implement both addition and subtraction (of "unsigned" bit strings):
Addition: The result has as many fractional bits as the term that has most fractional bits, and possibly one more integer bit than the term that has most integer bits. Start at the least significant bit in either term, and work your way up to the most significant bit in either term, summing each bit, and keeping the "carry bit" along, just like you'd do by hand. If the carry is nonzero at end, you'll get that one additional bit.
Subtraction: Always subtract smaller from larger. If that changes the order of the terms, negate the result. The result has at most as many fractional bits as the term that has most fractional bits, and at most as many integer bits as the term that has most integer bits. This is just like addition, except you subtract the bits, and instead of "carry bit", you use a "borrow bit". Because you subtract smaller unsigned value from larger unsigned value, the "borrow bit" will be zero at end.
Multiplication: The integer part has the number of integer bits, and the number of fractional bits, as the terms have in total (summed). You can implement the operation as if multiplying two unsigned integer values, and just insert the bit at end. (So that the result has as many fractional bits as the input terms have in total.) This usually involves a double loop, just like in long multiplication by hand.
Note that the same logic also works if you use larger radix instead of 2. The "carry"/"borrow" is a digit, between zero and one less than the radix.
Personally, I'd be very tempted to use a structure to describe each digit string:
typedef struct {
int idigits; /* Number of integral digits before point */
int fdigits; /* Number of fractional digits after point */
int size; /* Number of chars dynamically allocated at data */
char *point; /* Location of decimal point */
char *data; /* Dynamically allocated buffer */
} digitstring;
#define DIGITSTRING_INIT { 0, 0, 0, NULL, NULL }
with an additional flag if negative digit strings are to be supported.
Digit D with numerical value D×Bi, where B is the radix (number of unique digits used) and i being the position of said digit, is located at point[-i] if i < 0 (and -i <= fdigits), or at point[-i-1] if i >= 0 (and i < idigits). point[0] itself is where the decimal point is, if there is one.
For example, if we have string 0100.00, then idigits = 4, fdigits = 2, and the only nonzero digit is at position 2. (Position 0 is on the left side of the decimal point, and -1 on the right side.)
size and data fields allow reuse of the dynamically allocated buffer. Each declaration of a digitstring must be initialized, digitstring value = DIGITSTRING_INIT;, because there is no initialization function; this way you are less likely to leak memory (unless you forget to free a digitstring when no longer needed):
/* Free the specified digit string. */
static inline void digitstring_free(digitstring *ds)
{
if (ds) {
if (ds->data)
free(ds->data);
ds->idigits = 0;
ds->fdigits = 0;
ds->size = 0;
ds->point = NULL;
ds->data = NULL;
}
}
To use the digit string as a C string, you use a helper function to obtain the pointer to the most significant digit in the digit string:
/* Return a pointer to a printable version of the digit string. */
static const char *digitstring_str(const digitstring *ds, const char *none)
{
if (ds && ds->point)
return (const char *)(ds->point - ds->idigits);
else
return none;
}
I've found that rather than crash, it is often useful to pass an extra parameter that is only used for the return value when the return value is otherwise undefined. For example, if you have an initialized digit string foo without any contents, then digitstring_str(&foo, "0") returns the string literal "0".
The main point of the digit string structure is to have accessor functions that get and set each individual digit:
/* Get the value of a specific digit. */
static inline unsigned int digitstring_get(const digitstring *ds, const int position)
{
if (ds) {
if (position < 0) {
if (-position <= ds->fdigits)
return digit_to_value(ds->point[-position]);
else
return 0;
} else {
if (position < ds->idigits)
return digit_to_value(ds->point[-position-1]);
else
return 0;
}
} else
return 0;
}
/* Set the value of a specific digit. */
static inline void digitstring_set(digitstring *ds, const int position, const unsigned int value)
{
if (!ds) {
fprintf(stderr, "digitstring_set(): NULL digitstring specified.\n");
exit(EXIT_FAILURE);
} else
if (position < 0) {
if (-position > ds->fdigits) {
fprintf(stderr, "digitstring_set(): Digit position underflow (in fractional part).\n");
exit(EXIT_FAILURE);
}
ds->point[-position] = value_to_digit(value);
} else {
if (position >= ds->idigits) {
fprintf(stderr, "digitstring_set(): Digit position overflow (in integer part).\n");
exit(EXIT_FAILURE);
}
ds->point[-position-1] = value_to_digit(value);
}
}
Above, value_to_digit() is a helper function that converts a numerical value to the corresponding character, and digit_to_value() converts a character to the corresponding numerical value.
All operations (from parsing to arithmetic operators) really need a "constructor", that creates a new digit string with sufficient number of digits. (The number of digits is known beforehand for each operation, and depends only on the number of significant digits in the terms.) For this, I created a function that constructs a zero of desired size:
/* Clear the specified digit string to zero. */
static inline void digitstring_zero(digitstring *ds, int idigits, int fdigits)
{
int size;
char *data;
if (!ds) {
fprintf(stderr, "digitstring_zero(): No digitstring specified.\n");
exit(EXIT_FAILURE);
}
/* Require at least one integral digit. */
if (idigits < 1)
idigits = 1;
if (fdigits < 0)
fdigits = 0;
/* Total number of chars needed, including decimal point
and string-terminating nul char. */
size = idigits + 1 + fdigits + 1;
/* Check if dynamically allocated buffer needs resizing. */
if (ds->size < size) {
if (ds->data)
data = realloc(ds->data, size);
else
data = malloc(size);
if (!data) {
fprintf(stderr, "digitstring_zero(): Out of memory.\n");
exit(EXIT_FAILURE);
}
ds->data = data;
ds->size = size;
} else {
data = ds->data;
size = ds->size;
}
/* Fill it with zeroes. */
memset(data, value_to_digit(0), idigits + 1 + fdigits);
/* Pad the unused space with nul chars, terminating the string. */
memset(data + idigits + 1 + fdigits, '\0', size - idigits - 1 - fdigits);
/* Assign the decimal point. */
ds->point = data + idigits;
/* If there are no decimals, no need for a decimal point either. */
if (fdigits > 0)
ds->point[0] = decimal_point;
else
ds->point[0] = '\0';
/* After setting the desired digits, use digitstring_trim(). */
ds->idigits = idigits;
ds->fdigits = fdigits;
}
It will ensure the digit string has enough room for the specified number of digits, reallocating its dynamically allocated buffer if necessary, reusing it if already large enough.
The idea is that to implement an operation, you first find out the maximum number of integral and fractional digits the result can have. You use the above to create the result digit string, then digitstring_set() to set each digit to their respective values. You will typically operate in increasing digit significance, which means increasing digit "positions".
If we have additional helper functions int digits(const char *src), which returns the number of consecutive valid digit characters starting at src, and int decimal_points(const char *src), which returns 1 if src points to a decimal point, and 0 otherwise, we can parse input strings into digit strings using
/* Parse a string into a digit string, returning the pointer
to the first unparsed character, or NULL if an error occurs. */
static const char *digitstring_parse(digitstring *ds, const char *src)
{
const int zero = value_to_digit(0);
const char *idigit, *fdigit;
int idigits, fdigits, fextra, n;
/* Fail if nothing to parse. */
if (!src)
return NULL;
/* Skip leading whitespace. */
while (isspace((unsigned char)(*src)))
src++;
/* Fail if nothing to parse. */
if (*src == '\0')
return NULL;
/* Scan integer digits. */
idigit = src;
src += digits(src);
idigits = (int)(src - idigit);
/* Decimal point? */
fextra = 0;
n = decimal_points(src);
if (n > 0) {
src += n;
/* Scan fractional digits. */
fdigit = src;
src += digits(src);
fdigits = (int)(src - fdigit);
if (fdigits < 1)
fextra = 1;
} else {
fdigit = src;
fdigits = 0;
}
/* No digits? */
if (idigit == 0 && fdigit == 0)
return NULL;
/* Trim leading zeroes. */
while (idigits > 1 && *idigit == zero) {
idigits--;
idigit++;
}
/* Trim trailing zeroes. */
while (fdigits > 1 && fdigit[fdigits - 1] == zero)
fdigits--;
/* Create the necessary digit string, */
digitstring_zero(ds, idigits, fdigits + fextra);
/* copy the integer digits, if any, */
if (idigits > 0)
memcpy(ds->point - idigits, idigit, idigits);
/* and the fractional digits, if any. */
if (fdigits > 0)
memcpy(ds->point + 1, fdigit, fdigits);
/* Return a pointer to the first unparsed character. */
return src;
}
After updating its digits, one can call a helper function to remove any extra leading zeroes:
static inline void digitstring_ltrim(digitstring *ds)
{
if (ds && ds->point) {
const int zero = value_to_digit(0);
while (ds->idigits > 1 && ds->point[-ds->idigits] == zero)
ds->idigits--;
}
}
Adding two (unsigned) digit strings, possibly reusing one of the terms, is now quite simple to implement:
static void digitstring_add(digitstring *to, const digitstring *src1, const digitstring *src2)
{
digitstring result = DIGITSTRING_INIT;
unsigned int carry = 0;
int i, idigits, fdigits;
if (!to || !src1 || !src2) {
fprintf(stderr, "digitstring_add(): NULL digitstring specified.\n");
exit(EXIT_FAILURE);
}
/* For addition, the result has as many digits
as the longer source term. */
idigits = (src1->idigits >= src2->idigits) ? src1->idigits : src2->idigits;
fdigits = (src1->fdigits >= src2->fdigits) ? src1->fdigits : src2->fdigits;
/* Result needs possibly one more integer digit,
in case carry overflows. */
digitstring_zero(&result, idigits + 1, fdigits);
/* Addition loop, in order of increasing digit significance. */
for (i = -fdigits; i < idigits; i++) {
const unsigned int sum = digitstring_get(src1, i)
+ digitstring_get(src2, i)
+ carry;
digitstring_set(&result, i, sum % RADIX);
carry = sum / RADIX;
}
digitstring_set(&result, idigits, carry);
/* Trim leading zeroes. */
digitstring_ltrim(&result);
/* At this point, we can discard the target, even if it is actually
one of the sources, and copy the result to it. */
digitstring_free(to);
*to = result;
}
where RADIX is the radix used (the number of unique digits, 2 for binary). Pay extra attention to the digit loop. -fdigits is the least significant position in the result, and idigits-1 the most significant position. We need the accessor functions, because the source terms might not contain those digits at all (they are logically zero then).
These functions have been tested to work on both binary and octal number strings. I like this implementation, because it omits the decimal point if all terms are integers (so you get 12 + 33 = 45), but (due to fextra in digitstring_parse()) if any of the terms have a decimal point, then the result will have at least one fractional digit (so 12. + 33 = 45.0).
After all the beautiful ideas in Animals' answer I felt the strange urge, to present my own solution:
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define MAX(x, y) ((x) > (y) ? (x) : (y))
size_t gpp(char const *s)
{
char *n = strchr(s, '.');
return n ? n - s + 1 : 0;
}
char* bin_add(char const *a, char const *b)
{
char const *inp[] = { a, b };
size_t ll[] = { strlen(a), strlen(b) };
size_t pp[] = { gpp(a), gpp(b) };
size_t OO[2], off[2];
for (size_t i = 0; i < 2; ++i) {
OO[i] = pp[i] ? pp[i] - 1 : ll[i];
pp[i] = pp[i] ? ll[i] - pp[i] : 0;}
for (size_t i = 0; i < 2; ++i)
off[i] = OO[i] < OO[!i] ? OO[!i] - OO[i] : 0;
size_t ML = MAX(OO[0], OO[1]) + MAX(pp[0], pp[1]) + (!!pp[0] || !!pp[1]);
char *Ol = calloc(ML + 2, 1);
if(!Ol) return Ol;
char ops[2];
int xc = 0;
size_t lO = ML;
unsigned cc[2] = { 0 };
for (size_t i = ML; i; --i) {
bool pt = false;
for (size_t l = 0; l < 2; ++l) {
ops[l] = i <= ll[l] + off[l] && i - off[l] - 1
< ll[l] ? inp[l][i - off[l] - 1] : '0';
if (ops[l] == '.') {
if (cc[l]) {
free(Ol);
return NULL;
}
pt = true;
++cc[l];
}
ops[l] -= '0';
}
if (pt) {
Ol[i] = '.';
continue;
}
if ((Ol[i] = ops[0] + ops[1] + xc) > 1) {
Ol[i] = 0;
xc = 1;
}
else xc = 0;
lO = (Ol[i] += '0') == '1' ? i : lO;
}
if((Ol[0] = '0' + xc) == '1') return Ol;
for (size_t i = 0; i <= ML - lO + 1; ++i)
Ol[i] = Ol[lO + i];
return Ol;
}
int main(void)
{
char a[81], b[81];
while (scanf(" %80[0.1] %80[0.1]", a, b) & 1 << 1) {
char *c = bin_add(a, b);
if (!c && errno == ENOMEM) {
fputs("Not enough memory :(\n\n", stderr);
return EXIT_FAILURE;
}
else if (!c) {
fputs("Input error :(\n\n", stderr);
goto clear;
}
char* O[] = { a, b, c };
size_t lO[3], Ol = 0;
for (size_t i = 0; i < 3; ++i) {
lO[i] = gpp(O[i]);
lO[i] = lO[i] ? lO[i] : strlen(i[O]) + 1;
Ol = lO[i] > Ol ? lO[i] : Ol;
}
putchar('\n');
for (size_t i = 0; i < 3; ++i) {
for (size_t l = 0; l < Ol - lO[i]; ++l, putchar(' '));
puts(O[i]);
}
putchar('\n');
free(c);
clear :{ int c; while ((c = getchar()) != '\n' && c != EOF); }
}
}
Sample Output:
11001001 .11001001
11001001
.11001001
11001001.11001001
11001001 1010
11001001
1010
11010011
111111 1
111111
1
1000000
1010101 010111001.0101110101010
1010101
010111001.0101110101010
100001110.0101110101010
1001001.010111010101 10100101100.10010111101
1001001.010111010101
10100101100.10010111101
10101110101.111000001111
. .
.
.
0
.. .
Input error :(
A
Press any key to continue . . .
I contemplated wheter I should ask for a review at codereview. But I think I schould rather not.
There are two answers, depending upon whether you desire fixed- or floating- point arithmetic.
The first issue is reading the number. strtol() is your friend here:
char input[BUFFER_SIZE];
char * tmp;
long integral, fractional;
fgets(input, BUFFER_SIZE-1, stdin);
integral = strtol(input, &tmp, 2); /* Read BINARY integral part */
tmp++; /* Pass over the binary point. You may want to check that it is actually a dot */
fractional = strtol(tmp, null, 2); /* Read BINARY fractional part */
The next issue is figuring out how you will do the arithmetic. fractional must be bit-shifted an amount depending on how many digits past the point were provided and your desired precision. Fixed point arithmetic is simple: fractional <<= FRAC_BITS - strlen(tmp) then add the fractional parts together. Mask by ((1<<FRAC_BITS)-1) for the fractional part of the sum, shift the remaining bits and add them to the integral parts for the integral part of the sum. Floating-point is a little more finicky, but not too much harder.
For real numbers, convert non-fraction and fraction part to decimal, do the addition and print it as binary. This will require function to convert a number to binary string. Just a note that real numbers are float numbers in C and they are represented in binary with mantessa form like 2e^3 which is 2 multiplied by exponent to the power of 3.
I would like to produce a function which takes an integer x and char array in, and returns a string x steps into the sequence.
For example, consider the alphabet 'abc', which would produce the strings a, b, c, aa, ab, ac, ba, bb, bc, ca, cb, cc, aaa, aab... If the index 0 was passed in, I would expect the output to be 'a'; likewise, if the index 34 was passed in, I would expect the output 'cbb'.
For the alphabet '0123456789' I would expect the strings 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11...
I have written the following thus far, but am getting stuck on cases 21-23, 33-35, 45-47 where the behaviour deviates and I've been staring at this for a number of hours now without a pattern jumping out at me (with respect to the alphabet size and index). At first I didn't notice the issue, using a larger sized alphabet until it created bigger issues further in my program.
I'm not going to pretend the code below is in anyway elegant, following good practice, nor optimised - at this stage I really just want to understand the correct implementation of this pattern and have been changing things all over the place to attempt to resolve the issue. Apologies in advance if the variable names are confusing. Also, is this a common pattern/issue? I have tried to search for similar algorithms but have been unable to find anything with the terms that come to mind.
unsigned long power(int num, int exp)
{
int i;
unsigned long ret = num;
if (exp == 0) return 1;
for (i = 1; i < exp; i++)
{
ret *= num;
}
return ret;
}
unsigned long sumsqr(int base, int exp)
{
unsigned long sum;
for (sum = 0; exp > 0; exp--)
{
sum += power(base, exp);
}
return sum;
}
char * generateStringT(unsigned long index, char * charmap)
{
unsigned long scaler;
unsigned long remainder;
unsigned long divisor;
int base;
int exponent;
int factor;
char * buffer;
char * string;
int i;
buffer = malloc(sizeof(char) * 100);
i = 0;
base = strlen(charmap);
exponent = 0;
divisor = 0;
remainder = index;
while(sumsqr(base, exponent) <= index)
{
exponent++;
}
exponent--;
factor = exponent;
while(factor >= 0)
{
divisor = power(base, factor);
if ((factor > 1) && (exponent > 0))
divisor += power(base, factor-1);
scaler = remainder/divisor;
remainder = remainder - scaler * divisor;
printf("%lu,", scaler);
if ((factor == exponent) && (exponent > 0)) scaler--;
buffer[i++] = charmap[scaler];
factor--;
}
buffer[i++] = '\0';
string = malloc((strlen(buffer) + 1) * sizeof(char));
strcpy(string, buffer);
free(buffer);
return string;
}
What you are trying to do there looks like a base conversion, but actually is slightly different. Any number in any base can be thought as if they have infinitely many preceding zeros (or whatever the least significant digit is at that base) behind the represented number. This is not true in your case.
In your case, you lay importance to the amount of digits on the number you represent, making it slightly more complicated to index them. With bases in maths, it is easy to calculate the index of a represented number in any base b; that is, sum of the rank times the base raised to the power of order for each digit. In your case, the index builds up an additional sum_{k = 1}^{amount.of.digits.on.our.number - 1} base^k. If we subtract that addition from the index, our task becomes rather easy.
That addition can be calculated using your sumsqr function.
Here, I have changed your code just a little, with comments at where I've done changes, which is able to resolve many, just like you expect it to:
// added this
remainder -= sumsqr(base, exponent);
while (factor >= 0)
{
divisor = power(base, factor);
// commented this out
// if ((factor > 1) && (exponent > 0))
// divisor += power(base, factor - 1);
scaler = remainder/divisor;
remainder = remainder - scaler * divisor;
printf("%lu,", scaler);
// commented this out
// if ((factor == exponent) && (exponent > 0))
// scaler--;
buffer[i++] = charmap[scaler];
factor--;
}
I am not exactly sure what you were trying to do with the parts I've commented out. My guess is that you were trying to increase the divisor by that amount of difference I've talked previously, instead of decreasing the index or remainder by that amount.
Hope this helps in any way.
Not a fix (at a glance, your code uses a similar idea -- but more complicated!), but this is the code I used to convert an integer index to an a,b,c-format page number:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *number_alpha (char *dest, int value, char *base)
{
char *ddest = dest, *startdest = dest, swop;
if (value < 0)
{
value = -value;
*dest = '-';
startdest++;
ddest++;
}
value++;
do
{
*ddest = base[((value-1) % strlen(base))];
ddest++;
value = (value-1)/strlen(base);
} while (value > 0);
*ddest = 0;
ddest--;
while (ddest > startdest)
{
swop = *ddest;
*ddest = *startdest;
*startdest = swop;
startdest++;
ddest--;
}
return dest;
}
int main (int argc, char **argv)
{
int number;
char result[256];
if (argc != 3)
{
printf ("usage: [number] [string]\n");
return -1;
}
number = strtol (argv[1], NULL, 10);
number_alpha (result, number, argv[2]);
printf ("%d in 'base' %s yields %s\n", number, argv[2], result);
return 0;
}
It is very similar to the common task 'convert an integer to decimal notation'. By removing the value++ and changing (value-1) twice to just value in number_alpha, you get a bog-standard Int-To-Ascii routine. This one is special because the "wrap" occurs at a different place: for a base of 0123456789, incrementing 9 shows 00, not 10.
Sample outputs:
0 in 'base' abc yields a
34 in 'base' abc yields cbb
34 in 'base' 0123456789 yields 24
-34 in 'base' abc yields -cbb
9 in 'base' 0123456789 yields 9
10 in 'base' 0123456789 yields 00
--
See Translate a column index into an Excel Column Name for a couple of implementations in other languages. They seem to focus on recursive solutions, where mine is linear (for better or worse).
I've just started learning C and I'm having some problems with some code I want to write.
Basically I have this struct that is a bit array, with the number of bits in the array, and a pointer to a buffer of chars, that stores the bits.
My strategy for rotating the bit array is simply taking the number of rotations (mod the length to avoid full rotations) and using a simple reversal algorithm to rotate the array.
EDIT:
However, my problem is that I want to rotate the bits in the actual buffer.
I also want to be able to rotate a subsequence of bits within the entire bit array. So for 1101101, I might want to rotate (0-indexed from the left) the subsequence starting at index 2 and ending at index 5. I'm not entirely sure how to use my char buffer to do this.
Thanks for the help!
struct arrayBits{
size_t numBits;
char *buf;
}
The buf array holds 8-bit integers, not bools as I previously mentioned.
The way that I can access and set an individual bit is just by indexing into the byte that holds the bit I want (so for an array ab, ab->buf[index_of_desired_bit/8] and then performing some bitwise operations on it to change the value, for performance reasons.
EDIT: Thanks to everyone for all the suggestions. I've looked at all of them and I believe I understand the code better. Here's the code I ended up writing, however, I think there are some problems with it.
While it passes some of my basic test cases, it seems to run a little too fast on an bitarray of size 98775 bits, randomly filled. By this I mean, is there some case in which my code just outright fails and crashes? The test cases do three rotations, in a row, on the full 98775-bit array. One rotation of -98775/4 (<--this is a size_t, so wrap around?), one rotation of 98775/4, and then a final rotation of 98775/2.
Is there something I'm missing or some problem I'm not seeing?
/*Reverse a bit array*/
/*v1.1: basic bit reversal w/o temp variable*/
static void arrayReversal(bitarray_t *ba, size_t begin, size_t end){
while(begin < end)
{
bitarray_set(ba, begin, (bitarray_get(ba, begin) ^ bitarray_get(ba, end))); /*x = x ^ y*/
bitarray_set(ba, end, (bitarray_get(ba, begin) ^ bitarray_get(ba, end))); /*y = x ^ y*/
bitarray_set(ba, begin, (bitarray_get(ba, begin) ^ bitarray_get(ba, end))); /*x = x ^ y*/
begin++;
end--;
}
}
/*Main Rotation Routine*/
void bitarray_rotate(bitarray_t *ba, size_t bit_off, size_t bit_len, ssize_t bit_right_amount) {
assert(bit_off + bit_len <= ba->bit_sz);
assert(bit_off + bit_len > 0);
if(bit_off + bit_len > ba->bit_sz || bit_off + bit_len < 0)
{
printf("\nError: Indices out of bounds\n");
return;
}
/*Find index to split bitarray on*/
if(bit_len == 0) return; //Rotate only 1 bit i.e. no rotation
size_t reversal_index;
reversal_index = modulo(-bit_right_amount, bit_len);
if(reversal_index == 0) return; //No rotation to do
/*3 bit string reversals*/
assert(reversal_index - 1 + bit_off < ba->bit_sz);
/* Reverse A*/
arrayReversal(ba, bit_off, reversal_index - 1 + bit_off);
assert(reversal_index + bit_off < ba->bit_sz);
/*Reverse B*/
arrayReversal(ba, reversal_index + bit_off, (bit_off + bit_len - 1));
/*Reverse ArBr*/
arrayReversal(ba, bit_off, (bit_off + bit_len -1));
}
Well the easy way to start is to consider how to rotate the bits in a single value. Let's say that you have x, which is an N-bit value and you want to rotate it by k places. (I'm only going to look at rotating upwards/left, it is easy to convert to downwards/right). The first thing to observe is that if k=N then x is unchanged. So before rotating we want to reduce k modulo N to throw away complete rotations.
Next we should observe that during the rotation the k upper-bits will move to the bottom of the value, and the lower N-k bits will move up k places. This is the same as saying that the top k-bits move down N-k places. The reason that we phrase it this way is that C has shift operators, but not rotation.
In psuedo-C we can say:
#define N sizeof(type)*8
type rotate(type x, int k) {
type lower = x & ((1 << (N-k)) - 1);
type upper = x >> (N-k) & ((1 <<k)-1);
return upper | lower;
}
This takes care of the simple atomic case, simply replace type with char or int as appropriate. If type is unsigned then the mask on the value of upper is unnecessary.
The next thing to consider is rotating in an array of values. If you think of the above code as glueing together two halves of a value then for the more complicated case we need to glue together upper and lower parts from different places in the array. If k is small then these places are adjacent in the array, but when k>N we are rotating through more than one intermediate word.
In particular if we are rotating up k places then we are moving bits from k/N words away in the array, and the N bits can span floor(k/N) and ceil(k/N) locations away in the array. Ok, so now we're ready to put it all together. For each word in the array the new upper N-(k mod N) bits will be the lower bits of floor(k/N) words away, and the new lower (k mod N) bits will be the upper bits of ceil(k/N) words away.
In the same psuedo-C (i.e replace type with what you are using) we can say:
#define N sizeof(type)*8
#define ARR_SIZE ...
type rotate(type *x, int k,type *out) {
int r = k % N;
int upperOff = k/N;
int lowerOff = (k+N-1)/N;
for(int i=0; i<ARR_SIZE; i++) {
int lowerPos = (i + ARR_SIZE - lowerOff) % ARR_SIZE
int upperPos = (i + ARR_SIZE - upperOff) % ARR_SIZE
type lower = x[lowerPos] & ((1 << (N-k)) - 1)
type upper = x[upperPos] >> (N-k) & ((1 <<k)-1)
out[i] = upper | lower;
}
}
Anyway, that's a lot more than I was intending to write so I'll quit now. It should be easy enough to convert this to a form that works inplace on a single array, but you'll probably want to fix the types and the range of k first in order to bound the temporary storage.
If you have any more problems in this area then one place to look is bitmap sprite graphics. For example this rotation problem was used to implement scrolling many, many moons ago in 8-bit games.
I would suggest a pointer/offset to a starting point of a bit in the buffer instead of rotating. Feel free to overload any operator that might be useful, operator[] comes to mind.
A rotate(n) would simply be a offset+=n operation. But I find the purpose of your comment about -"However, my problem is that I want to rotate the actual buffer" confusing.
You dont need an extra buffer for rotate (only for output).
You should implement a function for one rotate and loop this, eg: (right-shift variation)
char *itoa2(char *s,size_t i)
{
*s=0;
do {
memmove(s+1,s,strlen(s)+1);
*s='0'+(i&1);
} while( i>>=1 );
return s;
}
size_t bitrotateOne(size_t i)
{
return i>>1 | (i&1) << (sizeof i<<3)-1;
}
...
size_t i=12,num=17;
char b[129];
while( num-- )
{
i = bitrotateOne(i);
puts( itoa2(b,i) );
}
Since your criteria is so complex, I think the easiest way to do it would be to step through each bit and set where it would be in your new array. You could speed it up for some operations by copying a whole character if it is outside the shifted bits, but I can't think of how to reliably do shifting taking into account all the variables because the start and end of the shifted sequence can be in the middle of bytes and so can the end of the entire bits. The key is to get the new bit position for a bit in the old array:
j = (i < startBit || i >= startBit + length) ? i :
((i - startBit + shiftRightCount) % length) + startBit;
Code:
#include "stdafx.h"
#include <stdlib.h>
#include <string.h>
typedef struct {
size_t numBits;
unsigned char *buf;
} ARRAYBITS;
// format is big endian, shiftint left 8 bits will shift all bytes to a lower index
ARRAYBITS rotateBits(ARRAYBITS *pOriginalBits, int startBit, int length, int shiftRightCount);
void setBit(unsigned char *buf, int bit, bool isSet);
bool checkBit(unsigned char *buf, int bit);
ARRAYBITS fromString(char *onesAndZeros);
char *toString(ARRAYBITS *pBits);
int _tmain(int argc, _TCHAR* argv[])
{
char input[1024];
ARRAYBITS bits = fromString("11110000110010101110"); // 20 bits
ARRAYBITS bitsA = rotateBits(&bits, 0, bits.numBits, 1);
ARRAYBITS bitsB = rotateBits(&bits, 0, bits.numBits, -1);
ARRAYBITS bitsC = rotateBits(&bits, 6, 8, 4);
ARRAYBITS bitsD = rotateBits(&bits, 6, 8, -2);
ARRAYBITS bitsE = rotateBits(&bits, 6, 8, 31);
ARRAYBITS bitsF = rotateBits(&bits, 6, 8, -31);
printf("Starting : %s\n", toString(&bits));
printf("All right 1: %s\n", toString(&bitsA));
printf("All left 1 : %s\n", toString(&bitsB));
printf("\n");
printf(" : ********\n");
printf("Starting : %s\n", toString(&bits));
printf("6,8,4 : %s\n", toString(&bitsC));
printf("6,8,-2 : %s\n", toString(&bitsD));
printf("6,8,31 : %s\n", toString(&bitsE));
printf("6,8,-31 : %s\n", toString(&bitsF));
gets(input);
}
ARRAYBITS rotateBits(ARRAYBITS *pOriginalBits, int startBit, int length, int shiftRightCount)
{
// 0-8 == 1, 9-16 == 2, 17-24 == 3
ARRAYBITS newBits;
int i = 0, j = 0;
int bytes = 0;
while (shiftRightCount < 0)
shiftRightCount += length;
shiftRightCount = shiftRightCount % length;
newBits.numBits = pOriginalBits->numBits;
if (pOriginalBits->numBits <= 0)
return newBits;
bytes = ((pOriginalBits->numBits -1) / 8) + 1;
newBits.buf = (unsigned char *)malloc(bytes);
memset(newBits.buf, 0, bytes);
for (i = 0; i < pOriginalBits->numBits; i++) {
j = (i < startBit || i >= startBit + length) ? i : ((i - startBit + shiftRightCount) % length) + startBit;
if (checkBit(pOriginalBits->buf, i))
{
setBit(newBits.buf, j, true);
}
}
return newBits;
}
void setBit(unsigned char *buf, int bit, bool isSet)
{
int charIndex = bit / 8;
unsigned char c = 1 << (bit & 0x07);
if (isSet)
buf[charIndex] |= c;
else
buf[charIndex] &= (c ^ 255);
}
bool checkBit(unsigned char *buf, int bit)
{
// address of char is (bit / 8), bit within char is (bit & 7)
int index = bit / 8;
int b = bit & 7;
int value = 1 << b;
return ((buf[index] & value) > 0);
}
ARRAYBITS fromString(char *onesAndZeros)
{
int i;
ARRAYBITS bits;
int charCount;
bits.numBits = strlen(onesAndZeros);
charCount = ((bits.numBits -1) / 8) + 1;
bits.buf = (unsigned char *)malloc(charCount);
memset(bits.buf, 0, charCount);
for (i = 0; i < bits.numBits; i++)
{
if (onesAndZeros[i] != '0')
setBit(bits.buf, i, true);
}
return bits;
}
char *toString(ARRAYBITS *pBits)
{
char *buf = (char *)malloc(pBits->numBits + 1);
int i;
for (i = 0; i < pBits->numBits; i++)
{
buf[i] = checkBit(pBits->buf, i) ? '1' : '0';
}
buf[i] = 0;
return buf;
}
I suggest you use bit-level operations (>>,<<,~,&,|) rather than wasting space using int. Even so, using an int array, to rotate, pass the left & right index of substring:
void rotate ( struct arrayBits a, int left , int right )
{
int i;
int first_bit;
if(*( a.buf + right ) == 1) first_bit = 1;
else first_bit = 0;
for( i = left+1 ; i <= right ; i++ )
{
*( a.buf + i )=*( a.buf + i - 1 );
}
*a.buf = first_bit;
}
Example:
If struct_array is 010101,
rotate (struct_array,0,5); => rotates whole string 1 int to right
o/p: 101010
rotate (struct_array,2,4); => rotates substring 1 int to right
o/p: 01 001 1
To reverse the bit array call the rotate() function on the substring, size_of_substring times.