-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
/
Copy pathdecimal_to_any_base.c
169 lines (153 loc) · 4.2 KB
/
decimal_to_any_base.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
* @file
* @author [jucollet972](https://github.com/jucollet972)
* @brief [Decimal to any-base](http://codeofthedamned.com/index.php/number-base-conversion) is a C function wich convert positive decimal
* integer to any positive ascii base with the base's alphabet given in input and return it in a dynamically allocated string(recursive way)
*/
#include <stdio.h> /// for IO operations
#include <string.h> /// for strchr and strlen
#include <stdint.h> /// for CPU arch's optimized int types
#include <stdbool.h> /// for boolean types
#include <assert.h> /// for assert
#include <stdlib.h> /// for malloc and free
/**
* @brief Checking if alphabet is valid
* @param base alphabet inputed by user
* @return int64_t as success or not
*/
bool isbad_alphabet(const char* alphabet) {
uint64_t len = strlen(alphabet);
/* Checking th lenght */
if (len < 2) {
return true;
}
/* Browse the alphabet */
for (int i = 0; i < len ; i++) {
/* Searching for duplicates */
if (strchr(alphabet + i + 1, alphabet[i]))
return true;
}
return false;
}
/**
* @brief Calculate the final length of the converted number
* @param nb to convert
* @param base calculated from alphabet
* @return Converted nb string length
*/
uint64_t converted_len(uint64_t nb, short base) {
/* Counting the number of characters translated to the base*/
if (nb > base - 1) {
return (converted_len(nb/base, base) + 1);
}
return 1;
}
/**
* @brief Convert positive decimal integer into anybase recursively
* @param nb to convert
* @param alphabet inputed by user used for base convertion
* @param base calculated from alphabet
* @param converted string filled with the convertion's result
* @return void
*/
void convertion(uint64_t nb, const char* alphabet, short base, char* converted) {
/* Recursive convertion */
*(converted) = *(alphabet + nb%base);
if (nb > base - 1) {
convertion(nb/base, alphabet, base, --converted);
}
}
/**
* @brief decimal_to_anybase ensure the validity of the parameters and convert any unsigned integers into any ascii positive base
* @param nb to convert
* @param base's alphabet
* @returns nb converted on success
* @returns NULL on error
*/
char* decimal_to_anybase(uint64_t nb, const char* alphabet) {
char* converted;
/* Verify that alphabet is valid */
if (isbad_alphabet(alphabet)) {
return NULL;
}
/* Convertion */
uint64_t base = strlen(alphabet);
uint64_t final_len = converted_len(nb, base);
converted = malloc(sizeof(char) * (final_len + 1));
converted[final_len] = 0;
convertion(nb, alphabet, base, converted + final_len - 1);
return converted;
}
/**
* @brief Self-test implementations
* @returns void
*/
static void test()
{
char* ret = NULL;
char* reference = NULL;
/* min dec*/
reference = "0";
ret = decimal_to_anybase(0, "0123456789");
for (int i = 0; i < strlen(reference) && i < strlen(ret); i++) {
assert(ret[i] == reference[i]);
}
if (ret != NULL) {
free(ret);
}
/* max dec*/
reference = "18446744073709551615";
ret = decimal_to_anybase(18446744073709551615, "0123456789");
for (int i = 0; i < strlen(reference) && i < strlen(ret); i++) {
assert(ret[i] == reference[i]);
}
if (ret != NULL) {
free(ret);
}
/* negative dec*/
reference = "18446744073709551615";
ret = decimal_to_anybase(-1, "0123456789");
for (int i = 0; i < strlen(reference) && i < strlen(ret); i++) {
assert(ret[i] == reference[i]);
}
if (ret != NULL) {
free(ret);
}
/* bin */
reference = "101010";
ret = decimal_to_anybase(42, "01");
for (int i = 0; i < strlen(reference) && i < strlen(ret); i++) {
assert(ret[i] == reference[i]);
}
if (ret != NULL) {
free(ret);
}
/* octal */
reference = "52";
ret = decimal_to_anybase(42, "01234567");
for (int i = 0; i < strlen(reference) && i < strlen(ret); i++) {
assert(ret[i] == reference[i]);
}
if (ret != NULL) {
free(ret);
}
/* hexa */
reference = "2A";
ret = decimal_to_anybase(42, "0123456789ABCDEF");
for (int i = 0; i < strlen(reference) && i < strlen(ret); i++) {
assert(ret[i] == reference[i]);
}
if (ret != NULL) {
free(ret);
}
printf("[+] All tests have successfully passed!\n");
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main()
{
test(); // run self-test implementations
return 0;
}