forked from alexpevzner/sane-airscan
-
Notifications
You must be signed in to change notification settings - Fork 0
/
airscan-math.c
216 lines (178 loc) · 4.38 KB
/
airscan-math.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/* AirScan (a.k.a. eSCL) backend for SANE
*
* Copyright (C) 2019 and up by Alexander Pevzner ([email protected])
* See LICENSE for license terms and conditions
*
* Miscellaneous mathematical functions
*/
#include "airscan.h"
#pragma GCC diagnostic ignored "-Wunused-result"
/* Find greatest common divisor of two positive integers
*/
SANE_Word
math_gcd (SANE_Word x, SANE_Word y)
{
log_assert(NULL, x > 0 && y > 0);
while (x != y) {
if (x > y) {
x -= y;
} else {
y -= x;
}
}
return x;
}
/* Find least common multiple of two positive integers
*/
SANE_Word
math_lcm (SANE_Word x, SANE_Word y)
{
return (x * y) / math_gcd(x, y);
}
/* Check two ranges for equivalency
*/
static inline bool
math_range_eq (const SANE_Range *r1, const SANE_Range *r2)
{
return r1->min == r2->min && r1->max == r2->max && r1->quant == r2->quant;
}
/* Check two ranges for overlapping
*/
static inline bool
math_range_ovp (const SANE_Range *r1, const SANE_Range *r2)
{
return r1->max >= r2->min && r2->max >= r1->min;
}
/* Merge two ranges, if possible
*/
bool
math_range_merge (SANE_Range *out, const SANE_Range *r1, const SANE_Range *r2)
{
/* Check for trivial cases */
if (math_range_eq(r1, r2)) {
*out = *r1;
return true;
}
if (!math_range_ovp(r1, r2)) {
return false;
}
/* Ranges have equal quantization? If yes, just adjust min and max */
if (r1->quant == r2->quant) {
out->min = math_max(r1->min, r2->min);
out->max = math_min(r1->max, r2->max);
out->quant = r1->quant;
return true;
}
/* At least one of ranges don't have quantization? */
if (!r1->quant || !r2->quant) {
/* To avoid code duplication, normalize things, so
* r1 does have quantization and r2 doesn't. Note,
* situation when both ranges don't have quantization
* was covered before, when we checked for equal quantization
*/
if (r1->quant == 0) {
const SANE_Range *tmp = r1;
r1 = r2;
r2 = tmp;
}
/* And fit r2 within r1 */
out->min = math_range_fit(r1, r2->min);
out->max = math_range_fit(r1, r2->max);
out->quant = r1->quant;
return true;
}
/* Now the most difficult case */
SANE_Word quant = math_lcm(r1->quant, r2->quant);
SANE_Word min, max, bounds_min, bounds_max;
min = math_min(r1->min, r2->min);
bounds_min = math_max(r1->min, r2->min);
bounds_max = math_min(r1->max, r2->max);
for (min = math_min(r1->min, r2->min); min < bounds_min; min += quant)
;
if (min > bounds_max) {
return false;
}
for (max = min; max + quant <= bounds_max; max += quant)
;
out->min = min;
out->max = max;
out->quant = quant;
return true;
}
/* Choose nearest integer in range
*/
SANE_Word
math_range_fit(const SANE_Range *r, SANE_Word i)
{
if (i < r->min) {
return r->min;
}
if (i > r->max) {
return r->max;
}
if (r->quant == 0) {
return i;
}
i -= r->min;
i = ((i + r->quant / 2) / r->quant) * r->quant;
i += r->min;
return math_min(i, r->max);
}
/* Format millimeters, for printing
*/
char*
math_fmt_mm (SANE_Word mm, char buf[])
{
double mmd = SANE_UNFIX(mm);
double integer, fraction;
integer = floor(mmd);
fraction = mmd - integer;
if (fraction != 0) {
sprintf(buf, "%d.%2.2d", (int) integer, (int) round(fraction * 100));
} else {
sprintf(buf, "%d", (int) integer);
}
return buf;
}
/* Genrate random 32-bit integer
*/
uint32_t
math_rand_u32 (void)
{
uint32_t r;
rand_bytes(&r, sizeof(r));
return r;
}
/* Generate random integer in range [0...max], inclusively
*/
uint32_t
math_rand_max (uint32_t max)
{
uint32_t mask, tmp;
for (mask = 0, tmp = max; tmp != 0; tmp >>= 1) {
mask |= tmp;
}
do {
tmp = math_rand_u32() & mask;
} while (tmp > max);
return tmp;
}
/* Generate random integer in range [min...max], inclusively
*/
uint32_t
math_rand_range (uint32_t min, uint32_t max)
{
/* Normalize range */
if (min == max) {
return min;
}
if (min > max) {
uint32_t tmp = max;
max = min;
min = tmp;
}
/* Generate random number */
return min + math_rand_max(max - min);
}
/* vim:ts=8:sw=4:et
*/