Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/graphite/src/Intervals.cpp
Line
Count
Source
1
// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
2
// Copyright 2010, SIL International, All rights reserved.
3
4
#include <algorithm>
5
#include <cmath>
6
#include <limits>
7
8
#include "inc/Intervals.h"
9
#include "inc/Segment.h"
10
#include "inc/Slot.h"
11
#include "inc/debug.h"
12
#include "inc/bits.h"
13
14
using namespace graphite2;
15
16
#include <cmath>
17
18
inline
19
0
Zones::Exclusion  Zones::Exclusion::split_at(float p) {
20
0
    Exclusion r(*this);
21
0
    r.xm = x = p;
22
0
    return r;
23
0
}
24
25
inline
26
0
void Zones::Exclusion::left_trim(float p) {
27
0
    x = p;
28
0
}
29
30
inline
31
0
Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) {
32
0
    c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false;
33
0
    return *this;
34
0
}
35
36
inline
37
0
uint8 Zones::Exclusion::outcode(float val) const {
38
0
    float p = val;
39
    //float d = std::numeric_limits<float>::epsilon();
40
0
    float d = 0.;
41
0
    return ((p - xm >= d) << 1) | (x - p > d);
42
0
}
43
44
0
void Zones::exclude_with_margins(float xmin, float xmax, int axis) {
45
0
    remove(xmin, xmax);
46
0
    weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false);
47
0
    weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false);
48
0
}
49
50
namespace
51
{
52
53
inline
54
0
bool separated(float a, float b) {
55
0
    return a != b;
56
    //int exp;
57
    //float res = frexpf(fabs(a - b), &exp);
58
    //return (*(unsigned int *)(&res) > 4);
59
    //return std::fabs(a-b) > std::numeric_limits<float>::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests
60
    //return std::fabs(a-b) > 0.5f;
61
0
}
62
63
}
64
65
void Zones::insert(Exclusion e)
66
0
{
67
#if !defined GRAPHITE2_NTRACING
68
    addDebug(&e);
69
#endif
70
0
    e.x = max(e.x, _pos);
71
0
    e.xm = min(e.xm, _posm);
72
0
    if (e.x >= e.xm) return;
73
74
0
    for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i)
75
0
    {
76
0
        const uint8 oca = e.outcode(i->x),
77
0
                    ocb = e.outcode(i->xm);
78
0
        if ((oca & ocb) != 0) continue;
79
80
0
        switch (oca ^ ocb)  // What kind of overlap?
81
0
        {
82
0
        case 0:     // e completely covers i
83
            // split e at i.x into e1,e2
84
            // split e2 at i.mx into e2,e3
85
            // drop e1 ,i+e2, e=e3
86
0
            *i += e;
87
0
            e.left_trim(i->xm);
88
0
            break;
89
0
        case 1:     // e overlaps on the rhs of i
90
            // split i at e->x into i1,i2
91
            // split e at i.mx into e1,e2
92
            // trim i1, insert i2+e1, e=e2
93
0
            if (!separated(i->xm, e.x)) break;
94
0
            if (separated(i->x,e.x))   { i = _exclusions.insert(i,i->split_at(e.x)); ++i; }
95
0
            *i += e;
96
0
            e.left_trim(i->xm);
97
0
            break;
98
0
        case 2:     // e overlaps on the lhs of i
99
            // split e at i->x into e1,e2
100
            // split i at e.mx into i1,i2
101
            // drop e1, insert e2+i1, trim i2
102
0
            if (!separated(e.xm, i->x)) return;
103
0
            if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
104
0
            *i += e;
105
0
            return;
106
0
        case 3:     // i completely covers e
107
            // split i at e.x into i1,i2
108
            // split i2 at e.mx into i2,i3
109
            // insert i1, insert e+i2
110
0
            if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
111
0
            i = _exclusions.insert(i, i->split_at(e.x));
112
0
            *++i += e;
113
0
            return;
114
0
        }
115
116
0
        ie = _exclusions.end();
117
0
    }
118
0
}
119
120
121
void Zones::remove(float x, float xm)
122
0
{
123
#if !defined GRAPHITE2_NTRACING
124
    removeDebug(x, xm);
125
#endif
126
0
    x = max(x, _pos);
127
0
    xm = min(xm, _posm);
128
0
    if (x >= xm) return;
129
130
0
    for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i)
131
0
    {
132
0
        const uint8 oca = i->outcode(x),
133
0
                    ocb = i->outcode(xm);
134
0
        if ((oca & ocb) != 0)   continue;
135
136
0
        switch (oca ^ ocb)  // What kind of overlap?
137
0
        {
138
0
        case 0:     // i completely covers e
139
0
            if (separated(i->x, x))  { i = _exclusions.insert(i,i->split_at(x)); ++i; }
140
0
            GR_FALLTHROUGH;
141
            // no break
142
0
        case 1:     // i overlaps on the rhs of e
143
0
            i->left_trim(xm);
144
0
            return;
145
0
        case 2:     // i overlaps on the lhs of e
146
0
            i->xm = x;
147
0
            if (separated(i->x, i->xm)) break;
148
0
            GR_FALLTHROUGH;
149
            // no break
150
0
        case 3:     // e completely covers i
151
0
            i = _exclusions.erase(i);
152
0
            --i;
153
0
            break;
154
0
        }
155
156
0
        ie = _exclusions.end();
157
0
    }
158
0
}
159
160
161
Zones::const_iterator Zones::find_exclusion_under(float x) const
162
0
{
163
0
    size_t l = 0, h = _exclusions.size();
164
165
0
    while (l < h)
166
0
    {
167
0
        size_t const p = (l+h) >> 1;
168
0
        switch (_exclusions[p].outcode(x))
169
0
        {
170
0
        case 0 : return _exclusions.begin()+p;
171
0
        case 1 : h = p; break;
172
0
        case 2 :
173
0
        case 3 : l = p+1; break;
174
0
        }
175
0
    }
176
177
0
    return _exclusions.begin()+l;
178
0
}
179
180
181
float Zones::closest(float origin, float & cost) const
182
0
{
183
0
    float best_c = std::numeric_limits<float>::max(),
184
0
          best_x = 0;
185
186
0
    const const_iterator start = find_exclusion_under(origin);
187
188
    // Forward scan looking for lowest cost
189
0
    for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i)
190
0
        if (i->track_cost(best_c, best_x, origin)) break;
191
192
    // Backward scan looking for lowest cost
193
    //  We start from the exclusion to the immediate left of start since we've
194
    //  already tested start with the right most scan above.
195
0
    for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i)
196
0
        if (i->track_cost(best_c, best_x, origin)) break;
197
198
0
    cost = (best_c == std::numeric_limits<float>::max() ? -1 : best_c);
199
0
    return best_x;
200
0
}
201
202
203
// Cost and test position functions
204
205
0
bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const {
206
0
    const float p = test_position(origin),
207
0
                localc = cost(p - origin);
208
0
    if (open && localc > best_cost) return true;
209
210
0
    if (localc < best_cost)
211
0
    {
212
0
        best_cost = localc;
213
0
        best_pos = p;
214
0
    }
215
0
    return false;
216
0
}
217
218
inline
219
0
float Zones::Exclusion::cost(float p) const {
220
0
    return (sm * p - 2 * smx) * p + c;
221
0
}
222
223
224
0
float Zones::Exclusion::test_position(float origin) const {
225
0
    if (sm < 0)
226
0
    {
227
        // sigh, test both ends and perhaps the middle too!
228
0
        float res = x;
229
0
        float cl = cost(x);
230
0
        if (x < origin && xm > origin)
231
0
        {
232
0
            float co = cost(origin);
233
0
            if (co < cl)
234
0
            {
235
0
                cl = co;
236
0
                res = origin;
237
0
            }
238
0
        }
239
0
        float cr = cost(xm);
240
0
        return cl > cr ? xm : res;
241
0
    }
242
0
    else
243
0
    {
244
0
        float zerox = smx / sm + origin;
245
0
        if (zerox < x) return x;
246
0
        else if (zerox > xm) return xm;
247
0
        else return zerox;
248
0
    }
249
0
}
250
251
252
#if !defined GRAPHITE2_NTRACING
253
254
void Zones::jsonDbgOut(Segment *seg) const {
255
256
    if (_dbg)
257
    {
258
        for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s)
259
        {
260
            *_dbg << json::flat << json::array
261
                << objectid(dslot(seg, (Slot *)(s->_env[0])))
262
                << reinterpret_cast<ptrdiff_t>(s->_env[1]);
263
            if (s->_isdel)
264
                *_dbg << "remove" << Position(s->_excl.x, s->_excl.xm);
265
            else
266
                *_dbg << "exclude" << json::flat << json::array
267
                    << s->_excl.x << s->_excl.xm
268
                    << s->_excl.sm << s->_excl.smx << s->_excl.c
269
                    << json::close;
270
            *_dbg << json::close;
271
        }
272
    }
273
}
274
275
#endif