source: branches/alilink/SL/INSDEL/insdel.cxx

Last change on this file was 18126, checked in by westram, 5 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 101.3 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : insdel.cxx                                        //
4//   Purpose   : insert/delete columns                             //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11// AISC_MKPT_PROMOTE:#ifndef ARBDB_BASE_H
12// AISC_MKPT_PROMOTE:#include <arbdb_base.h>
13// AISC_MKPT_PROMOTE:#endif
14
15#include "insdel.h"
16#include <RangeList.h>
17
18#include <arbdbt.h>
19#include <adGene.h>
20#include <arb_progress.h>
21#include <arb_defs.h>
22#include <arb_diff.h>
23#include <algorithm>
24
25using namespace std;
26
27#define id_assert(cond) arb_assert(cond)
28
29// --------------------------------------------------------------------------------
30// helper to hold any kind of unit (char, int, float)
31
32class UnitPtr {
33    const void *ptr;
34public:
35    UnitPtr() : ptr(NULp) {}
36    UnitPtr(const void *ptr_)
37        : ptr(ptr_)
38    {
39        id_assert(ptr);
40    }
41
42    void set_pointer(const void *ptr_) {
43        id_assert(!ptr);
44        ptr = ptr_;
45    }
46    const void *get_pointer() const { return ptr; }
47    const void *expect_pointer() const { id_assert(ptr); return ptr; }
48};
49struct UnitPair {
50    UnitPtr left, right;
51};
52
53template <typename T>
54inline int compare_type(const T& t1, const T& t2) {
55    return t1<t2 ? -1 : (t1>t2 ? 1 : 0);
56}
57
58// --------------------------------------------------------------------------------
59
60class AliData;
61typedef SmartPtr<AliData> AliDataPtr;
62
63// --------------------------------------------------------------------------------
64
65class AliData {
66    size_t          size;
67    static GB_ERROR op_error;
68
69public:
70    AliData(size_t size_) : size(size_) {}
71    virtual ~AliData() {}
72
73    virtual size_t unitsize() const = 0;
74    virtual bool has_slice() const = 0;
75
76    enum memop {
77        COPY_TO, // always returns 0
78        COMPARE_WITH, // returns compare value
79        CHECK_DELETE, // return 0 if ok to delete, otherwise op_error is set
80    };
81
82    void clear_error() const { op_error = NULp; }
83    void set_error(GB_ERROR error) const {
84        id_assert(error);
85        id_assert(!op_error);
86        op_error = error;
87    }
88
89    virtual int operate_on_mem(void *mem, size_t start, size_t count, memop op) const           = 0;
90    virtual int cmp_data(size_t start, const AliData& other, size_t ostart, size_t count) const = 0;
91
92    void copyPartTo(void *mem, size_t start, size_t count) const { operate_on_mem(mem, start, count, COPY_TO); }
93    int cmpPartWith(const void *mem, size_t start, size_t count) const {
94        id_assert(is_valid_part(start, count));
95        return operate_on_mem(const_cast<void*>(mem), start, count, COMPARE_WITH); // COMPARE_WITH does not modify
96    }
97    GB_ERROR check_delete_allowed(size_t start, size_t count) const {
98        op_error = NULp;
99        id_assert(start <= size);
100        IF_ASSERTION_USED(int forbidden =) operate_on_mem(NULp, start, std::min(count, size-start), CHECK_DELETE);
101        id_assert(correlated(forbidden, op_error));
102        return op_error;
103    }
104
105    virtual UnitPtr unit_left_of(size_t pos) const  = 0;
106    virtual UnitPtr unit_right_of(size_t pos) const = 0;
107
108    virtual AliDataPtr create_gap(size_t gapsize, const UnitPair& gapinfo) const = 0;
109    virtual AliDataPtr slice_down(size_t start, size_t count) const = 0;
110
111    size_t elems() const { return size; }
112    size_t memsize() const { return unitsize()*elems(); }
113    void copyTo(void *mem) const { copyPartTo(mem, 0, elems()); }
114    bool empty() const { return !elems(); }
115
116    int cmp_whole_data(const AliData& other) const {
117        int cmp = cmp_data(0, other, 0, std::min(elems(), other.elems()));
118        if (cmp == 0) { // prefixes are equal
119            return compare_type(elems(), other.elems());
120        }
121        return cmp;
122    }
123
124    bool equals(const AliData& other) const {
125        if (&other == this) return true;
126        if (elems() != other.elems()) return false;
127
128        return cmp_whole_data(other) == 0;
129    }
130    bool differs_from(const AliData& other) const { return !equals(other); }
131
132    bool is_valid_pos(size_t pos) const { return pos < elems(); }
133    bool is_valid_between(size_t pos) const { return pos <= elems(); } // pos == 0 -> before first base; pos == elems() -> after last base
134
135    bool is_valid_part(size_t start, size_t count) const {
136        return is_valid_between(start) && is_valid_between(start+count);
137    }
138};
139
140GB_ERROR AliData::op_error = NULp;
141
142// --------------------------------------------------------------------------------
143
144class AliDataSlice : public AliData {
145    AliDataPtr from;
146    size_t     offset;
147
148    static int fix_amount(AliDataPtr from, size_t offset, size_t amount) {
149        if (amount) {
150            size_t from_size = from->elems();
151            if (offset>from_size) {
152                amount = 0;
153            }
154            else {
155                size_t last_pos  = offset+amount-1;
156                size_t last_from = from->elems()-1;
157
158                if (last_pos > last_from) {
159                    id_assert(last_from >= offset);
160                    amount = last_from-offset+1;
161                }
162            }
163        }
164        return amount;
165    }
166
167    AliDataSlice(AliDataPtr from_, size_t offset_, size_t amount_)
168        : AliData(fix_amount(from_, offset_, amount_)),
169          from(from_),
170          offset(offset_)
171    {
172        id_assert(!from->has_slice()); // do not double-slice
173    }
174
175public:
176    static AliDataPtr make(AliDataPtr from, size_t offset, size_t amount) {
177        return (offset == 0 && amount >= from->elems())
178            ? from
179            : (from->has_slice()
180               ? from->slice_down(offset, amount)
181               : new AliDataSlice(from, offset, amount));
182    }
183
184    size_t unitsize() const OVERRIDE { return from->unitsize(); }
185    bool has_slice() const OVERRIDE { return true; }
186
187    AliDataPtr create_gap(size_t gapsize, const UnitPair& gapinfo) const OVERRIDE {
188        return from->create_gap(gapsize, gapinfo);
189    }
190    AliDataPtr slice_down(size_t start, size_t count) const OVERRIDE {
191        return new AliDataSlice(from, offset+start, std::min(count, elems()));
192    }
193    int operate_on_mem(void *mem, size_t start, size_t count, memop op) const OVERRIDE {
194        id_assert(is_valid_part(start, count));
195        return from->operate_on_mem(mem, start+offset, count, op);
196    }
197    UnitPtr unit_left_of(size_t pos) const OVERRIDE {
198        id_assert(is_valid_between(pos));
199        return from->unit_left_of(pos+offset);
200    }
201    UnitPtr unit_right_of(size_t pos) const OVERRIDE {
202        id_assert(is_valid_between(pos));
203        return from->unit_right_of(pos+offset);
204    }
205    int cmp_data(size_t start, const AliData& other, size_t ostart, size_t count) const OVERRIDE {
206        id_assert(is_valid_part(start, count));
207        id_assert(other.is_valid_part(ostart, count));
208
209        return from->cmp_data(start+offset, other, ostart, count);
210    }
211};
212
213class ComposedAliData FINAL_TYPE : public AliData {
214    AliDataPtr left, right;
215    bool       hasSlice;
216
217    ComposedAliData(AliDataPtr l, AliDataPtr r)
218        : AliData(l->elems()+r->elems()),
219          left(l),
220          right(r),
221          hasSlice(left->has_slice() || right->has_slice())
222    {
223        id_assert(l->unitsize() == r->unitsize());
224        id_assert(l->elems());
225        id_assert(r->elems());
226    }
227    friend AliDataPtr concat(AliDataPtr left, AliDataPtr right); // for above ctor
228
229    void *inc_by_units(void *mem, size_t units) const { return reinterpret_cast<char*>(mem)+units*unitsize(); }
230
231public:
232    size_t unitsize() const OVERRIDE { return left->unitsize(); }
233    bool has_slice() const OVERRIDE { return hasSlice; }
234
235    AliDataPtr create_gap(size_t gapsize, const UnitPair& gapinfo) const OVERRIDE {
236        return left->create_gap(gapsize, gapinfo);
237    }
238    AliDataPtr slice_down(size_t start, size_t count) const OVERRIDE {
239        size_t left_elems = left->elems();
240
241        if (left_elems <= start) { // left is before slice
242            return AliDataSlice::make(right, start-left_elems, count);
243        }
244
245        size_t pos_behind = start+count;
246        if (left_elems >= pos_behind) { // right is behind slice
247            return AliDataSlice::make(left, start, min(count, left_elems));
248        }
249
250        size_t take_left  = left_elems-start;
251        size_t take_right = count-take_left;
252
253        return new ComposedAliData(
254            AliDataSlice::make(left, start, take_left),
255            AliDataSlice::make(right, 0, take_right)
256            );
257    }
258    int operate_on_mem(void *mem, size_t start, size_t count, memop op) const OVERRIDE {
259        size_t left_elems = left->elems();
260        size_t take_left  = 0;
261        int    res        = 0;
262        if (start<left_elems) {
263            take_left = min(count, left_elems-start);
264            res       = left->operate_on_mem(mem, start, take_left, op);
265        }
266
267        if (res == 0) {
268            size_t take_right = count-take_left;
269            if (take_right) {
270                size_t rstart = start>left_elems ? start-left_elems : 0;
271                id_assert(right->is_valid_part(rstart, take_right));
272                res = right->operate_on_mem(inc_by_units(mem, take_left), rstart, take_right, op);
273            }
274        }
275        return res;
276    }
277    int cmp_data(size_t start, const AliData& other, size_t ostart, size_t count) const OVERRIDE {
278        size_t left_elems = left->elems();
279        size_t take_left  = 0;
280        int    cmp        = 0;
281        if (start<left_elems) {
282            take_left = min(count, left_elems-start);
283            cmp       = left->cmp_data(start, other, ostart, take_left);
284        }
285
286        if (cmp == 0) {
287            size_t take_right = count-take_left;
288            if (take_right) {
289                size_t rstart  = start>left_elems ? start-left_elems : 0;
290                size_t rostart = ostart+take_left;
291
292                id_assert(is_valid_part(rstart, take_right));
293                id_assert(other.is_valid_part(rostart, take_right));
294
295                cmp = right->cmp_data(rstart, other, rostart, take_right);
296            }
297        }
298        return cmp;
299    }
300
301    UnitPtr unit_left_of(size_t pos) const OVERRIDE {
302        id_assert(is_valid_between(pos));
303        if (left->elems() == pos) { // split between left and right
304            id_assert(pos >= 1);
305            return left->unit_right_of(pos-1);
306        }
307        else if (left->elems() < pos) { // split inside or behind 'right'
308            return right->unit_left_of(pos-left->elems());
309        }
310        else { // split inside or frontof 'left'
311            return left->unit_left_of(pos);
312        }
313    }
314    UnitPtr unit_right_of(size_t pos) const OVERRIDE {
315        id_assert(is_valid_between(pos));
316        if (left->elems() == pos) { // split between left and right
317            id_assert(pos >= 1);
318            return right->unit_left_of(0);
319        }
320        else if (left->elems() < pos) { // split inside or behind 'right'
321            return right->unit_right_of(pos-left->elems());
322        }
323        else { // split inside or frontof 'left'
324            return left->unit_right_of(pos);
325        }
326    }
327};
328
329// --------------------------------------------------------------------------------
330
331class Deletable { // define characters allowed to delete (only applicable to TypedAliData<char>)
332    bool deletable[256];
333
334    void init(bool val) {
335        for (int i = 0; i<256; ++i) {
336            deletable[i] = val;
337        }
338    }
339
340public:
341    enum DeleteWhat { NOTHING, ANYTHING };
342    explicit Deletable(DeleteWhat what) {
343        switch (what) {
344            case ANYTHING: init(true); break;
345            case NOTHING: init(false); break;
346        }
347    }
348    explicit Deletable(const char *allowed) {
349        init(false);
350        for (int i = 0; allowed[i]; ++i) {
351            deletable[safeCharIndex(allowed[i])] = true;
352        }
353    }
354
355    GB_ERROR get_delete_error(const char *data, size_t start, size_t count) const {
356        GB_ERROR error = NULp;
357        id_assert(count > 0);
358        size_t   end   = start+count-1;
359        for (size_t col = start; col <= end && !error; ++col) {
360            if (!deletable[safeCharIndex(data[col])]) {
361                error = GBS_global_string("You tried to delete '%c' at position %zu  -> Operation aborted", data[col], col);
362            }
363        }
364        return error;
365    }
366};
367
368// --------------------------------------------------------------------------------
369
370template<typename T>
371class TypedAliData : public AliData {
372    T gap;
373
374protected:
375    static const T *typed_ptr(const UnitPtr& uptr) { return (const T*)uptr.get_pointer(); }
376    const T* std_gap_ptr() const { return &gap; }
377
378public:
379    TypedAliData(size_t size_, T gap_)
380        : AliData(size_),
381          gap(gap_)
382    {}
383
384    const T& std_gap() const { return gap; }
385
386    size_t unitsize() const OVERRIDE { return sizeof(T); }
387    bool has_slice() const OVERRIDE { return false; }
388
389    virtual UnitPtr at_ptr(size_t pos) const = 0;
390    AliDataPtr create_gap(size_t gapsize, const UnitPair& /*gapinfo*/) const OVERRIDE;
391    __ATTR__NORETURN AliDataPtr slice_down(size_t /*start*/, size_t /*count*/) const OVERRIDE {
392        GBK_terminate("logic error: slice_down called for explicit TypedAliData");
393    }
394    UnitPtr unit_left_of(size_t pos) const OVERRIDE {
395        id_assert(is_valid_between(pos));
396        return at_ptr(pos-1);
397    }
398    UnitPtr unit_right_of(size_t pos) const OVERRIDE {
399        id_assert(is_valid_between(pos));
400        return at_ptr(pos);
401    }
402};
403
404template<typename T>
405struct SpecificGap : public TypedAliData<T> {
406    typedef TypedAliData<T> BaseType;
407
408    SpecificGap(size_t gapsize, const T& gap_)
409        : BaseType(gapsize, gap_)
410    {}
411    int operate_on_mem(void *mem, size_t IF_ASSERTION_USED(start), size_t count, AliData::memop op) const OVERRIDE {
412        id_assert(BaseType::is_valid_part(start, count));
413        switch (op) {
414            case AliData::COPY_TO: {
415                T *typedMem = (T*)mem;
416                for (size_t a = 0; a<count; ++a) { // LOOP_VECTORIZED=3
417                    typedMem[a] = BaseType::std_gap();
418                }
419                break;
420            }
421            case AliData::COMPARE_WITH: {
422                const T *typedMem = (const T*)mem;
423                for (size_t a = 0; a<count; ++a) {
424                    int cmp = compare_type(BaseType::std_gap(), typedMem[a]);
425                    if (cmp) return cmp;
426                }
427                break;
428            }
429            case AliData::CHECK_DELETE: {
430                break; // deleting an inserted gap is always permitted
431            }
432        }
433        return 0;
434    }
435    int cmp_data(size_t start, const AliData& other, size_t ostart, size_t count) const OVERRIDE {
436        const SpecificGap<T> *other_is_gap = dynamic_cast<const SpecificGap<T>*>(&other);
437        if (other_is_gap) {
438            return compare_type(BaseType::std_gap(), other_is_gap->std_gap());
439        }
440        return -other.cmp_data(ostart, *this, start, count);
441    }
442    UnitPtr at_ptr(size_t pos) const OVERRIDE {
443        if (pos<BaseType::elems()) return UnitPtr(BaseType::std_gap_ptr());
444        return UnitPtr();
445    }
446};
447
448template <typename T>
449AliDataPtr TypedAliData<T>::create_gap(size_t gapsize, const UnitPair& /*gapinfo*/) const {
450    return new SpecificGap<T>(gapsize, std_gap());
451}
452
453class SizeAwarable {
454    bool   allows_oversize;
455    size_t org_ali_size;
456public:
457    SizeAwarable(bool allows_oversize_, size_t ali_size_)
458        : allows_oversize(allows_oversize_),
459          org_ali_size(ali_size_)
460    {}
461
462    size_t get_allowed_size(size_t term_size, size_t new_ali_size) const {
463        size_t allowed_size = new_ali_size;
464        if (allows_oversize && term_size>org_ali_size) {
465            size_t oversize = term_size-org_ali_size;
466            allowed_size    = new_ali_size+oversize;
467        }
468        return allowed_size;
469    }
470};
471inline SizeAwarable dontAllowOversize(size_t ali_size) { return SizeAwarable(false, ali_size); }
472
473template<typename T>
474inline GB_ERROR check_delete_allowed(const T *, size_t, size_t , const Deletable& ) {
475    return NULp; // for non-char deleting is always allowed
476}
477template<>
478inline GB_ERROR check_delete_allowed(const char *data, size_t start, size_t count, const Deletable& deletable) {
479    return deletable.get_delete_error(data, start, count);
480}
481
482template<typename T>
483class SpecificAliData : public TypedAliData<T>, public SizeAwarable, virtual Noncopyable {
484    const T   *data;
485    Deletable  deletable;
486
487public:
488    typedef TypedAliData<T> BaseType;
489
490    SpecificAliData(const T *static_data, size_t elements, const T& gap_, const SizeAwarable& sizeAware, const Deletable& deletable_)
491        : BaseType(elements, gap_),
492          SizeAwarable(sizeAware),
493          data(static_data),
494          deletable(deletable_)
495    {}
496
497    int operate_on_mem(void *mem, size_t start, size_t count, AliData::memop op) const OVERRIDE {
498        if (count>0) {
499            id_assert(BaseType::is_valid_part(start, count));
500            switch (op) {
501                case AliData::COPY_TO: {
502                    size_t msize = BaseType::unitsize()*count;
503                    id_assert(msize>0);
504                    memcpy(mem, data+start, msize);
505                    break;
506                }
507                case AliData::COMPARE_WITH: {
508                    const T *typedMem = (const T*)mem;
509                    for (size_t a = 0; a<count; ++a) {
510                        int cmp = compare_type(data[start+a], typedMem[a]);
511                        if (cmp) return cmp;
512                    }
513                    break;
514                }
515                case AliData::CHECK_DELETE: {
516                    const T  *typedMem = (const T*)data;
517                    GB_ERROR  error    = check_delete_allowed<T>(typedMem, start, count, deletable);
518                    if (error) {
519                        BaseType::set_error(error);
520                        return 1;
521                    }
522                    break;
523                }
524            }
525        }
526        return 0;
527    }
528    int cmp_data(size_t start, const AliData& other, size_t ostart, size_t count) const OVERRIDE {
529        id_assert(BaseType::is_valid_part(start, count));
530        id_assert(other.is_valid_part(ostart, count));
531
532        // if (&other == this && start == ostart) return true; // @@@ why does this fail tests?
533        return -other.cmpPartWith(data+start, ostart, count);
534    }
535    UnitPtr at_ptr(size_t pos) const OVERRIDE {
536        if (pos<BaseType::elems()) return UnitPtr(&data[pos]);
537        return UnitPtr();
538    }
539    const T *get_data() const { return data; }
540};
541
542class SequenceAliData : public SpecificAliData<char> {
543    char dot;
544
545    char preferred_gap(const char *s1, const char *s2) const {
546        if (s1 && s2) {
547            if (*s1 == std_gap() || *s2 == std_gap()) {
548                return std_gap();
549            }
550            if (*s1 == dot || *s2 == dot) {
551                return dot;
552            }
553            return std_gap();
554        }
555        else if (s1) {
556            id_assert(!s2);
557            return *s1 == std_gap() ? std_gap() : dot;
558        }
559        else if (s2) {
560            id_assert(!s1);
561            return *s2 == std_gap() ? std_gap() : dot;
562        }
563        else {
564            id_assert(!s1 && !s2);
565            return dot;
566        }
567    }
568
569public:
570    SequenceAliData(const char* static_data, size_t elements, char stdgap, char dotgap, const SizeAwarable& sizeAware, const Deletable& deletable_)
571        : SpecificAliData<char>(static_data, elements, stdgap, sizeAware, deletable_),
572          dot(dotgap)
573    {}
574
575    AliDataPtr create_gap(size_t gapsize, const UnitPair& gapinfo) const OVERRIDE {
576        char use = preferred_gap(typed_ptr(gapinfo.left), typed_ptr(gapinfo.right));
577        return new SpecificGap<char>(gapsize, use);
578    }
579};
580
581// --------------------------------------------------------------------------------
582// @@@ move things below into a class ?
583
584inline AliDataPtr concat(AliDataPtr left, AliDataPtr right) {
585    return left->empty() ? right : (right->empty() ? left : new ComposedAliData(left, right));
586}
587inline AliDataPtr concat(AliDataPtr left, AliDataPtr mid, AliDataPtr right) {
588    return concat(left, concat(mid, right));
589}
590
591inline AliDataPtr partof(AliDataPtr data, size_t pos, size_t amount) { return AliDataSlice::make(data, pos, amount); }
592inline AliDataPtr before(AliDataPtr data, size_t pos) { return partof(data, 0, pos); }
593inline AliDataPtr after(AliDataPtr data, size_t pos) { return partof(data, pos+1, data->elems()-pos-1); }
594
595inline AliDataPtr delete_from(AliDataPtr from, size_t pos, size_t amount, GB_ERROR& error) {
596    error = from->check_delete_allowed(pos, amount);
597    return concat(before(from, pos), after(from, pos+amount-1));
598}
599inline AliDataPtr insert_at(AliDataPtr dest, size_t pos, AliDataPtr src) {
600    return concat(before(dest, pos), src, after(dest, pos-1));
601}
602
603inline AliDataPtr insert_gap(AliDataPtr data, size_t pos, size_t count) {
604    UnitPair gapinfo;
605
606    id_assert(data->unitsize() <= sizeof(gapinfo.left));
607
608    gapinfo.left  = data->unit_left_of(pos); // @@@ do not perform ALWAYS (put into an object and lazy eval)
609    gapinfo.right = data->unit_right_of(pos);
610
611    AliDataPtr gap = data->create_gap(count, gapinfo);
612    return insert_at(data, pos, gap);
613}
614
615inline AliDataPtr format(AliDataPtr data, const size_t wanted_len, GB_ERROR& error) {
616    size_t curr_len = data->elems();
617    if (curr_len < wanted_len) {
618        data = insert_gap(data, curr_len, wanted_len-curr_len);
619    }
620    else if (curr_len > wanted_len) {
621        data = delete_from(data, wanted_len, curr_len-wanted_len, error);
622    }
623    id_assert(data->elems() == wanted_len);
624    return data;
625}
626
627
628template<typename T> inline AliDataPtr makeAliData(T*& allocated_data, size_t elems, const T& gap) {
629    return new SpecificAliData<T>(allocated_data, elems, gap, dontAllowOversize(elems), Deletable(Deletable::ANYTHING));
630}
631inline AliDataPtr makeAliSeqData(char*& allocated_data, size_t elems, char gap, char dot) {
632    return new SequenceAliData(allocated_data, elems, gap, dot, dontAllowOversize(elems), Deletable(Deletable::ANYTHING));
633}
634
635// --------------------------------------------------------------------------------
636
637#ifdef UNIT_TESTS
638#ifndef TEST_UNIT_H
639#include <test_unit.h>
640#endif
641
642template<typename T>
643inline T*& copyof(const T* const_data, size_t elemsize, size_t elements) { // @@@ elemsize should be derived from type here (if possible)
644    static T *copy = NULp;
645
646    size_t memsize = elemsize*elements;
647    id_assert(!copy);
648    copy = (T*)ARB_alloc<char>(memsize);
649    id_assert(copy);
650    memcpy(copy, const_data, memsize);
651    return copy;
652}
653
654#define COPYOF(typedarray) copyof(typedarray, sizeof(*(typedarray)), ARRAY_ELEMS(typedarray))
655#define SIZEOF(typedarray) (sizeof(*(typedarray))*ARRAY_ELEMS(typedarray))
656
657#define TEST_EXPECT_COPIES_EQUAL(d1,d2) do{                     \
658        size_t  s1 = (d1)->memsize();                           \
659        size_t  s2 = (d2)->memsize();                           \
660        TEST_EXPECT_EQUAL(s1, s2);                              \
661        void   *copy1 = ARB_alloc<char>(s1+s2);                 \
662        void   *copy2 = reinterpret_cast<char*>(copy1)+s1;      \
663        (d1)->copyTo(copy1);                                    \
664        (d2)->copyTo(copy2);                                    \
665        TEST_EXPECT_MEM_EQUAL(copy1, copy2, s1);                \
666        free(copy1);                                            \
667    }while(0)
668
669#define TEST_EXPECT_COPY_EQUALS_ARRAY(adp,typedarray,asize) do{ \
670        size_t  size    = (adp)->memsize();                     \
671        TEST_EXPECT_EQUAL(size, asize);                         \
672        void   *ad_copy = ARB_alloc<char*>(size);               \
673        (adp)->copyTo(ad_copy);                                 \
674        TEST_EXPECT_MEM_EQUAL(ad_copy, typedarray, size);       \
675        free(ad_copy);                                          \
676    }while(0)
677
678#define TEST_EXPECT_COPY_EQUALS_STRING(adp,str) do{             \
679        size_t  size    = (adp)->memsize();                     \
680        char   *ad_copy = ARB_alloc<char>(size+1);              \
681        (adp)->copyTo(ad_copy);                                 \
682        ad_copy[size]   = 0;                                    \
683        TEST_EXPECT_EQUAL(ad_copy, str);                        \
684        free(ad_copy);                                          \
685    }while(0)
686
687#if defined(ENABLE_CRASH_TESTS) && defined(ASSERTION_USED)
688static void illegal_alidata_composition() {
689    const int ELEMS = 5;
690
691    int  *i = ARB_alloc<int> (ELEMS);
692    char *c = ARB_alloc<char>(ELEMS);
693
694    concat(makeAliData(i, ELEMS, 0), makeAliData(c, ELEMS, '-'));
695}
696#endif
697
698void TEST_illegal_alidata__crashtest() {
699    TEST_EXPECT_CODE_ASSERTION_FAILS(illegal_alidata_composition);
700}
701
702template <typename T>
703inline T *makeCopy(AliDataPtr d) {
704    TEST_EXPECT_EQUAL(d->unitsize(), sizeof(T));
705    size_t  size = d->memsize();
706    T      *copy = (T*)ARB_alloc<char>(size);
707    d->copyTo(copy);
708    return copy;
709}
710
711template <typename T>
712static arb_test::match_expectation compare_works(AliDataPtr d1, AliDataPtr d2, int expected_cmp) {
713    int brute_force_compare = 0;
714    {
715        int minSize = std::min(d1->elems(), d2->elems());
716
717        T *copy1 = makeCopy<T>(d1);
718        T *copy2 = makeCopy<T>(d2);
719
720        for (int i = 0; i < minSize && brute_force_compare == 0; ++i) { // compare inclusive terminal zero-element
721            brute_force_compare = compare_type(copy1[i], copy2[i]);
722        }
723
724        if (brute_force_compare == 0) {
725            brute_force_compare = compare_type(d1->elems(), d2->elems());
726        }
727
728        free(copy2);
729        free(copy1);
730    }
731
732    int smart_forward_compare  = d1->cmp_whole_data(*d2);
733    int smart_backward_compare = d2->cmp_whole_data(*d1);
734
735    using namespace   arb_test;
736    expectation_group expected;
737
738    expected.add(that(brute_force_compare).is_equal_to(expected_cmp));
739    expected.add(that(smart_forward_compare).is_equal_to(expected_cmp));
740    expected.add(that(smart_backward_compare).is_equal_to(-expected_cmp));
741
742    return all().ofgroup(expected);
743}
744
745#define TEST_COMPARE_WORKS(d1,d2,expected) TEST_EXPECTATION(compare_works<char>(d1,d2,expected))
746
747#define TEST_COMPARE_WORKS_ALL_TYPES(tid,d1,d2,expected)                                \
748    switch (tid) {                                                              \
749        case 0: TEST_EXPECTATION(compare_works<char>(d1,d2,expected)); break;           \
750        case 1: TEST_EXPECTATION(compare_works<GB_UINT4>(d1,d2,expected)); break;       \
751        case 2: TEST_EXPECTATION(compare_works<float>(d1,d2,expected)); break;          \
752    }
753
754#if !defined(ENABLE_CRASH_TESTS)
755static void avoid_INVALID_testExport() { avoid_INVALID_testExport(); } // avoids weird symbol-export-bug with stabs + !ENABLE_CRASH_TESTS (encountered with gcc 4.4.3)
756#endif
757
758__ATTR__REDUCED_OPTIMIZE void TEST_AliData() {
759#define SEQDATA "CGCAC-C-GG-C-GG.A.-C------GG-.C..UCAGU"
760    char      chr_src[] = SEQDATA; // also contains trailing 0-byte!
761    GB_CUINT4 int_src[] = { 0x01, 0x1213, 0x242526, 0x37383930, 0xffffffff };
762    float     flt_src[] = { 0.0, 0.5, 1.0, -5.0, 20.1 };
763
764    AliDataPtr type[] = {
765        makeAliSeqData(COPYOF(chr_src), ARRAY_ELEMS(chr_src)-1, '-', '.'),
766        makeAliData(COPYOF(int_src), ARRAY_ELEMS(int_src), 0U),
767        makeAliData(COPYOF(flt_src), ARRAY_ELEMS(flt_src), 0.0F)
768    };
769    TEST_EXPECT_COPY_EQUALS_ARRAY(type[0], chr_src, SIZEOF(chr_src)-1);
770    TEST_EXPECT_COPY_EQUALS_STRING(type[0], chr_src);
771    TEST_EXPECT_COPY_EQUALS_ARRAY(type[1], int_src, SIZEOF(int_src));
772    TEST_EXPECT_COPY_EQUALS_ARRAY(type[2], flt_src, SIZEOF(flt_src));
773
774    for (size_t t = 0; t<ARRAY_ELEMS(type); ++t) {
775        AliDataPtr data  = type[t];
776        AliDataPtr dup = concat(data, data);
777        TEST_EXPECT_EQUAL(dup->elems(), 2*data->elems());
778
779        AliDataPtr start = before(data, 3);
780        TEST_EXPECT_EQUAL(start->elems(), 3U);
781
782        AliDataPtr end = after(data, 3);
783        TEST_EXPECT_EQUAL(end->elems(), data->elems()-4);
784
785        AliDataPtr mid = partof(data, 3, 1);
786        TEST_EXPECT_COPIES_EQUAL(concat(start, mid, end), data);
787
788        GB_ERROR   error = NULp;
789        AliDataPtr del   = delete_from(data, 3, 1, error);
790        TEST_EXPECT_NO_ERROR(error);
791        TEST_EXPECT_EQUAL(del->elems(), data->elems()-1);
792        TEST_EXPECT_COPIES_EQUAL(concat(start, end), del);
793
794        AliDataPtr empty = before(data, 0);
795        TEST_EXPECT_EQUAL(empty->elems(), 0U);
796
797        TEST_EXPECT_COPIES_EQUAL(data, concat(data, empty));
798        TEST_EXPECT_COPIES_EQUAL(data, concat(empty, data));
799        TEST_EXPECT_COPIES_EQUAL(empty, concat(empty, empty));
800
801        AliDataPtr del_rest = delete_from(data, 3, 999, error);
802        TEST_EXPECT_NO_ERROR(error);
803        TEST_EXPECT_COPIES_EQUAL(start, del_rest);
804
805        AliDataPtr ins = insert_at(del, 3, mid);
806        TEST_EXPECT_COPIES_EQUAL(data, ins);
807        TEST_EXPECT_COPIES_EQUAL(del, delete_from(ins, 3, 1, error));
808        TEST_EXPECT_NO_ERROR(error);
809
810        TEST_EXPECT_COPIES_EQUAL(insert_at(del, 3, empty), del);
811        TEST_EXPECT_COPIES_EQUAL(insert_at(del, 777, empty), del); // append via insert_at
812        TEST_EXPECT_COPIES_EQUAL(insert_at(start, 777, end), del); // append via insert_at
813
814        AliDataPtr ins_gap = insert_gap(del, 4, 5);
815        TEST_EXPECT_EQUAL(ins_gap->elems(), del->elems()+5);
816
817        AliDataPtr gap_iseq = partof(ins_gap, 4, 5);
818
819        TEST_EXPECT_COPIES_EQUAL(ins_gap, insert_gap(ins_gap, 7, 0)); // insert empty gap
820
821        AliDataPtr start_gap1 = insert_gap(ins_gap, 0, 1); // insert gap at start
822        AliDataPtr start_gap3 = insert_gap(ins_gap, 0, 3); // insert gap at start
823
824        AliDataPtr gap_iempty = insert_gap(empty, 0, 5);
825        TEST_EXPECT_EQUAL(gap_iempty->elems(), 5U);
826
827        AliDataPtr gap_in_gap = insert_gap(gap_iempty, 3, 2);
828        TEST_EXPECT_EQUAL(gap_in_gap->elems(), 7U);
829
830        AliDataPtr end_gap1 = insert_gap(mid, 1, 1);
831        TEST_EXPECT_EQUAL(end_gap1->elems(), 2U);
832
833        if (t == 0) {
834            AliDataPtr end_gap2 = insert_gap(end, 34, 2);
835
836            TEST_EXPECT_COPY_EQUALS_STRING(start,      "CGC");
837            TEST_EXPECT_COPY_EQUALS_STRING(end,        "C-C-GG-C-GG.A.-C------GG-.C..UCAGU");
838            TEST_EXPECT_COPY_EQUALS_STRING(end_gap2,   "C-C-GG-C-GG.A.-C------GG-.C..UCAGU..");
839            TEST_EXPECT_COPY_EQUALS_STRING(mid,        "A");
840            TEST_EXPECT_COPY_EQUALS_STRING(end_gap1,   "A-");      // '-' is ok, since before there was a C behind (but correct would be '.')
841            TEST_EXPECT_COPY_EQUALS_STRING(del,        "CGCC-C-GG-C-GG.A.-C------GG-.C..UCAGU");
842            TEST_EXPECT_COPY_EQUALS_STRING(del_rest,   "CGC");
843            TEST_EXPECT_COPY_EQUALS_STRING(ins,        "CGCAC-C-GG-C-GG.A.-C------GG-.C..UCAGU");
844            TEST_EXPECT_COPY_EQUALS_STRING(gap_iseq,   "-----");   // inserted between bases
845            TEST_EXPECT_COPY_EQUALS_STRING(gap_iempty, ".....");   // inserted in empty sequence
846            TEST_EXPECT_COPY_EQUALS_STRING(gap_in_gap, "......."); // inserted gap in gap
847            TEST_EXPECT_COPY_EQUALS_STRING(ins_gap,    "CGCC------C-GG-C-GG.A.-C------GG-.C..UCAGU");
848            TEST_EXPECT_COPY_EQUALS_STRING(start_gap1, ".CGCC------C-GG-C-GG.A.-C------GG-.C..UCAGU");
849            TEST_EXPECT_COPY_EQUALS_STRING(start_gap3, "...CGCC------C-GG-C-GG.A.-C------GG-.C..UCAGU");
850
851            AliDataPtr bef_dot     = insert_gap(ins, 15, 2);
852            AliDataPtr aft_dot     = insert_gap(ins, 16, 2);
853            AliDataPtr bet_dots    = insert_gap(ins, 32, 2);
854            AliDataPtr bet_dashes  = insert_gap(ins, 23, 2);
855            AliDataPtr bet_dashdot = insert_gap(ins, 29, 2);
856            AliDataPtr bet_dotdash = insert_gap(ins, 18, 2);
857
858            TEST_EXPECT_COPY_EQUALS_STRING(ins,        "CGCAC-C-GG-C-GG.A.-C------GG-.C..UCAGU");
859            TEST_EXPECT_COPY_EQUALS_STRING(bef_dot,    "CGCAC-C-GG-C-GG...A.-C------GG-.C..UCAGU");
860            TEST_EXPECT_COPY_EQUALS_STRING(aft_dot,    "CGCAC-C-GG-C-GG...A.-C------GG-.C..UCAGU");
861            TEST_EXPECT_COPY_EQUALS_STRING(bet_dots,   "CGCAC-C-GG-C-GG.A.-C------GG-.C....UCAGU");
862            TEST_EXPECT_COPY_EQUALS_STRING(bet_dashes, "CGCAC-C-GG-C-GG.A.-C--------GG-.C..UCAGU");
863            TEST_EXPECT_COPY_EQUALS_STRING(bet_dashdot,"CGCAC-C-GG-C-GG.A.-C------GG---.C..UCAGU");
864            TEST_EXPECT_COPY_EQUALS_STRING(bet_dotdash,"CGCAC-C-GG-C-GG.A.---C------GG-.C..UCAGU");
865
866            {
867                // test comparability of AliData
868
869                AliDataPtr same_as_start_gap1 = after(start_gap3, 1);
870
871                TEST_COMPARE_WORKS(start_gap1, same_as_start_gap1, 0);
872
873                TEST_EXPECT(start_gap1->differs_from(*start_gap3));
874                // TEST_EXPECT_EQUAL(strcmp(".CGCC------C-GG-C-GG.A.-C------GG-.C..UCAGU",        // start_gap1
875                                         // "...CGCC------C-GG-C-GG.A.-C------GG-.C..UCAGU"), 1); // start_gap3
876
877                TEST_EXPECT_EQUAL(start_gap1->cmp_whole_data(*start_gap3),  1);
878                TEST_EXPECT_EQUAL(start_gap3->cmp_whole_data(*start_gap1), -1);
879
880                TEST_COMPARE_WORKS(end, end_gap2, -1);
881            }
882        }
883
884        {
885            // test comparability of AliData (for all types)
886
887            TEST_COMPARE_WORKS_ALL_TYPES(t, start_gap1, start_gap3, 1);
888            TEST_COMPARE_WORKS_ALL_TYPES(t, gap_iempty, gap_in_gap, -1);
889            TEST_COMPARE_WORKS_ALL_TYPES(t, del, ins, 1);
890            TEST_COMPARE_WORKS_ALL_TYPES(t, partof(ins_gap, 0, 17), partof(start_gap3, 3, 17), 0);
891            TEST_COMPARE_WORKS_ALL_TYPES(t, start_gap3, start_gap3, 0);
892        }
893    }
894
895}
896
897#endif // UNIT_TESTS
898
899// --------------------------------------------------------------------------------
900
901enum TerminalType {
902    IDT_SPECIES = 0,
903    IDT_SAI,
904    IDT_SECSTRUCT,
905};
906
907static GB_CSTR targetTypeName[] = {
908    "Species",
909    "SAI",
910    "SeceditStruct",
911};
912
913class Alignment {
914    SmartCharPtr name; // name of alignment
915    size_t       len;  // length of alignment
916public:
917    Alignment(const char *name_, size_t len_) : name(strdup(name_)), len(len_) {}
918
919    const char *get_name() const { return &*name; }
920    size_t get_len() const { return len; }
921};
922
923// --------------------------------------------------------------------------------
924
925class AliApplicable { // something that can be appied to the whole alignment
926    virtual GB_ERROR apply_to_terminal(GBDATA *gb_data, TerminalType term_type, const char *item_name, const Alignment& ali) const = 0;
927
928    GB_ERROR apply_recursive(GBDATA *gb_data, TerminalType term_type, const char *item_name, const Alignment& ali) const;
929    GB_ERROR apply_to_childs_named(GBDATA *gb_item_data, const char *item_field, TerminalType term_type, const Alignment& ali) const;
930    GB_ERROR apply_to_secstructs(GBDATA *gb_secstructs, const Alignment& ali) const;
931
932public:
933    AliApplicable() {}
934    virtual ~AliApplicable() {}
935
936    GB_ERROR apply_to_alignment(GBDATA *gb_main, const Alignment& ali) const;
937};
938
939GB_ERROR AliApplicable::apply_recursive(GBDATA *gb_data, TerminalType term_type, const char *item_name, const Alignment& ali) const {
940    GB_ERROR error = NULp;
941    GB_TYPES type  = GB_read_type(gb_data);
942
943    if (type == GB_DB) {
944        GBDATA *gb_child;
945        for (gb_child = GB_child(gb_data); gb_child && !error; gb_child = GB_nextChild(gb_child)) {
946            error = apply_recursive(gb_child, term_type, item_name, ali);
947        }
948    }
949    else {
950        error = apply_to_terminal(gb_data, term_type, item_name, ali);
951    }
952
953    return error;
954}
955GB_ERROR AliApplicable::apply_to_childs_named(GBDATA *gb_item_data, const char *item_field, TerminalType term_type, const Alignment& ali) const {
956    GBDATA   *gb_item;
957    GB_ERROR  error      = NULp;
958    long      item_count = GB_number_of_subentries(gb_item_data);
959
960    if (item_count) {
961        for (gb_item = GB_entry(gb_item_data, item_field);
962             gb_item && !error;
963             gb_item = GB_nextEntry(gb_item))
964        {
965            GBDATA *gb_ali = GB_entry(gb_item, ali.get_name());
966            if (gb_ali) {
967                const char *item_name = GBT_read_name(gb_item);
968                error = apply_recursive(gb_ali, term_type, item_name, ali);
969                if (error) error = GBS_global_string("%s '%s': %s", targetTypeName[term_type], item_name, error);
970            }
971        }
972    }
973    return error;
974}
975GB_ERROR AliApplicable::apply_to_secstructs(GBDATA *gb_secstructs, const Alignment& ali) const {
976    GB_ERROR  error  = NULp;
977    GBDATA   *gb_ali = GB_entry(gb_secstructs, ali.get_name());
978
979    if (gb_ali) {
980        long item_count = GB_number_of_subentries(gb_ali)-1;
981        if (item_count<1) item_count = 1;
982
983        GBDATA *gb_item;
984        for (gb_item = GB_entry(gb_ali, "struct");
985             gb_item && !error;
986             gb_item = GB_nextEntry(gb_item))
987        {
988            GBDATA *gb_ref = GB_entry(gb_item, "ref");
989            if (gb_ref) {
990                error = apply_recursive(gb_ref, IDT_SECSTRUCT, "ref", ali);
991                if (error) {
992                    const char *item_name = GBT_read_name(gb_item);
993                    error = GBS_global_string("%s '%s': %s", targetTypeName[IDT_SECSTRUCT], item_name, error);
994                }
995            }
996        }
997    }
998    return error;
999}
1000
1001GB_ERROR AliApplicable::apply_to_alignment(GBDATA *gb_main, const Alignment& ali) const {
1002    GB_ERROR error    = apply_to_childs_named(GBT_find_or_create(gb_main, "extended_data", 7), "extended", IDT_SAI,     ali);
1003    if (!error) error = apply_to_secstructs(GB_search(gb_main, "secedit/structs", GB_CREATE_CONTAINER), ali);
1004    if (!error) error = apply_to_childs_named(GBT_find_or_create(gb_main, "species_data",  7), "species",  IDT_SPECIES, ali);
1005    return error;
1006}
1007
1008// --------------------------------------------------------------------------------
1009
1010class AliEntryCounter : public AliApplicable {
1011    mutable size_t count;
1012    GB_ERROR apply_to_terminal(GBDATA *, TerminalType, const char *, const Alignment&) const OVERRIDE { count++; return NULp; }
1013public:
1014    AliEntryCounter() : count(0) {}
1015    size_t get_entry_count() const { return count; }
1016};
1017
1018// --------------------------------------------------------------------------------
1019
1020struct AliEditCommand {
1021    virtual ~AliEditCommand() {}
1022    virtual AliDataPtr apply(AliDataPtr to, GB_ERROR& error) const                                 = 0;
1023    virtual GB_ERROR check_applicable_to(const Alignment& ali, size_t& resulting_ali_length) const = 0;
1024};
1025
1026class AliInsertCommand : public AliEditCommand {
1027    size_t pos; // inserts in front of pos
1028    size_t amount;
1029public:
1030    AliInsertCommand(size_t pos_, size_t amount_) : pos(pos_), amount(amount_) {}
1031    AliDataPtr apply(AliDataPtr to, GB_ERROR& /*error*/) const OVERRIDE { return insert_gap(to, pos, amount); }
1032    GB_ERROR check_applicable_to(const Alignment& ali, size_t& resulting_ali_length) const OVERRIDE {
1033        size_t len = ali.get_len();
1034        if (pos>len) {
1035            return GBS_global_string("Can't insert at position %zu (exceeds length %zu of alignment '%s')",
1036                                     pos, len, ali.get_name());
1037        }
1038        resulting_ali_length = len+amount;
1039        return NULp;
1040    }
1041};
1042
1043class AliDeleteCommand : public AliEditCommand {
1044    size_t pos;
1045    size_t amount;
1046public:
1047    AliDeleteCommand(size_t pos_, size_t amount_)
1048        : pos(pos_),
1049          amount(amount_)
1050    {}
1051    AliDataPtr apply(AliDataPtr to, GB_ERROR& error) const OVERRIDE { return delete_from(to, pos, amount, error); }
1052    GB_ERROR check_applicable_to(const Alignment& ali, size_t& resulting_ali_length) const OVERRIDE {
1053        size_t len     = ali.get_len();
1054        size_t end_pos = pos+amount-1;
1055        if (end_pos >= len) {
1056            return GBS_global_string("Can't delete positions %zu-%zu (exceeds max. position %zu of alignment '%s')",
1057                                     pos, end_pos, len-1, ali.get_name());
1058        }
1059        resulting_ali_length = len-amount;
1060        return NULp;
1061    }
1062};
1063
1064class AliFormatCommand FINAL_TYPE : public AliEditCommand {
1065    size_t wanted_len;
1066
1067public:
1068    AliFormatCommand(size_t wanted_len_) : wanted_len(wanted_len_) {}
1069    AliDataPtr apply(AliDataPtr to, GB_ERROR& error) const OVERRIDE {
1070        SizeAwarable *knows_size = dynamic_cast<SizeAwarable*>(&*to);
1071
1072        id_assert(knows_size); // format can only be applied to SpecificAliData
1073                               // i.e. AliFormatCommand has to be the FIRST of a series of applied commands!
1074
1075        int allowed_size = knows_size->get_allowed_size(to->elems(), wanted_len);
1076        return format(to, allowed_size, error);
1077    }
1078    GB_ERROR check_applicable_to(const Alignment& IF_ASSERTION_USED(ali), size_t& resulting_ali_length) const OVERRIDE {
1079        id_assert(ali.get_len() == wanted_len);
1080        resulting_ali_length     = wanted_len;
1081        return NULp;
1082    }
1083};
1084
1085class AliAutoFormatCommand : public AliEditCommand {
1086    mutable SmartPtr<AliFormatCommand> cmd;
1087public:
1088    AliDataPtr apply(AliDataPtr to, GB_ERROR& error) const OVERRIDE {
1089        return cmd->apply(to, error);
1090    }
1091    GB_ERROR check_applicable_to(const Alignment& ali, size_t& resulting_ali_length) const OVERRIDE {
1092        cmd = new AliFormatCommand(ali.get_len()); // late decision on length to format
1093        return cmd->check_applicable_to(ali, resulting_ali_length);
1094    }
1095};
1096
1097class AliCompositeCommand : public AliEditCommand, virtual Noncopyable {
1098    AliEditCommand *first;
1099    AliEditCommand *second;
1100public:
1101    AliCompositeCommand(AliEditCommand *cmd1_, AliEditCommand *cmd2_) // takes ownership of commands
1102        : first(cmd1_),
1103          second(cmd2_)
1104    {}
1105    ~AliCompositeCommand() OVERRIDE { delete second; delete first; }
1106    AliDataPtr apply(AliDataPtr to, GB_ERROR& error) const OVERRIDE {
1107        AliDataPtr tmp = first->apply(to, error);
1108        if (!error) tmp = second->apply(tmp, error);
1109        return tmp;
1110    }
1111    GB_ERROR check_applicable_to(const Alignment& ali, size_t& resulting_ali_length) const OVERRIDE {
1112        GB_ERROR error = first->check_applicable_to(ali, resulting_ali_length);
1113        if (!error) {
1114            Alignment tmp_ali(ali.get_name(), resulting_ali_length);
1115            error = second->check_applicable_to(tmp_ali, resulting_ali_length);
1116        }
1117        return error;
1118    }
1119};
1120
1121// --------------------------------------------------------------------------------
1122
1123class AliEditor : public AliApplicable {
1124    const AliEditCommand& cmd;
1125    Deletable             deletable;
1126
1127    mutable arb_progress progress;
1128    mutable size_t       modified_counter;
1129
1130    GB_ERROR apply_to_terminal(GBDATA *gb_data, TerminalType term_type, const char *item_name, const Alignment& ali) const OVERRIDE;
1131
1132    bool shall_edit(GBDATA *gb_data, TerminalType term_type) const {
1133        // defines whether specific DB-elements shall be edited by any AliEditor
1134        // (true for all data, that contains alignment position specific data)
1135
1136        const char *key   = GB_read_key_pntr(gb_data);
1137        bool        shall = key[0] != '_';                                    // general case: don't apply to keys starting with '_'
1138        if (!shall) shall = term_type == IDT_SAI && strcmp(key, "_REF") == 0; // exception (SAI:_REF needs editing)
1139        return shall;
1140    }
1141
1142public:
1143    AliEditor(const AliEditCommand& cmd_, const Deletable& deletable_, const char *progress_title, size_t progress_count)
1144        : cmd(cmd_),
1145          deletable(deletable_),
1146          progress(progress_title, progress_count),
1147          modified_counter(0)
1148    {}
1149    ~AliEditor() OVERRIDE {
1150        progress.done();
1151    }
1152
1153    const AliEditCommand& edit_command() const { return cmd; }
1154};
1155
1156// --------------------------------------------------------------------------------
1157
1158static char   *insDelBuffer = NULp;
1159static size_t  insDelBuffer_size;
1160
1161inline void free_insDelBuffer() {
1162    freenull(insDelBuffer);
1163}
1164inline char *provide_insDelBuffer(size_t neededSpace) {
1165    if (insDelBuffer && insDelBuffer_size<neededSpace) free_insDelBuffer();
1166    if (!insDelBuffer) {
1167        insDelBuffer_size = neededSpace+10;
1168        insDelBuffer      = ARB_alloc<char>(insDelBuffer_size);
1169    }
1170    return insDelBuffer;
1171}
1172
1173inline GB_CSTR alidata2buffer(const AliData& data) { // @@@ DRY vs copying code (above in this file)
1174    char *buffer = provide_insDelBuffer(data.memsize()+1);
1175
1176    data.copyTo(buffer);
1177    buffer[data.memsize()] = 0; // only needed for strings but does not harm otherwise
1178
1179    return buffer;
1180}
1181
1182// --------------------------------------------------------------------------------
1183
1184class EditedTerminal;
1185
1186class LazyAliData : public AliData, public SizeAwarable, virtual Noncopyable {
1187    // internally transforms into SpecificAliData as soon as somebody tries to access the data.
1188    // (implements lazy loading of sequence data, esp. useful when applying AliFormatCommand; see #702)
1189
1190    TerminalType       term_type;
1191    EditedTerminal&    terminal;
1192    mutable AliDataPtr loaded; // always is TypedAliData<T>
1193
1194    AliDataPtr loaded_data() const {
1195        if (loaded.isNull()) load_data();
1196        return loaded;
1197    }
1198
1199public:
1200    LazyAliData(const SizeAwarable& oversizable, size_t size_, TerminalType term_type_, EditedTerminal& terminal_)
1201        : AliData(size_),
1202          SizeAwarable(oversizable),
1203          term_type(term_type_),
1204          terminal(terminal_)
1205    {}
1206
1207    size_t unitsize() const OVERRIDE {
1208        // Note: information also known by EditedTerminal (only depends on data-type)
1209        // No need to load data (doesnt harm atm as data is always used for more atm)
1210        return loaded_data()->unitsize();
1211    }
1212    bool has_slice() const OVERRIDE {
1213        id_assert(loaded_data()->has_slice() == false); // TypedAliData<T> never has_slice()!
1214        return false;
1215    }
1216
1217    int operate_on_mem(void *mem, size_t start, size_t count, memop op) const OVERRIDE { return loaded_data()->operate_on_mem(mem, start, count, op); }
1218    int cmp_data(size_t start, const AliData& other, size_t ostart, size_t count) const OVERRIDE { return loaded_data()->cmp_data(start, other, ostart, count); }
1219
1220    UnitPtr unit_left_of(size_t pos) const OVERRIDE { return loaded_data()->unit_left_of(pos); }
1221    UnitPtr unit_right_of(size_t pos) const OVERRIDE { return loaded_data()->unit_right_of(pos); }
1222
1223    AliDataPtr create_gap(size_t gapsize, const UnitPair& gapinfo) const OVERRIDE { return loaded_data()->create_gap(gapsize, gapinfo); }
1224    __ATTR__NORETURN AliDataPtr slice_down(size_t /*start*/, size_t /*count*/) const OVERRIDE {
1225        GBK_terminate("logic error: slice_down called for explicit LazyAliData");
1226    }
1227
1228    void load_data() const; // has to be public to be a friend of EditedTerminal
1229};
1230
1231// --------------------------------------------------------------------------------
1232
1233class EditedTerminal : virtual Noncopyable {
1234    GBDATA     *gb_data;
1235    GB_TYPES    type;
1236    const char *item_name; // name of SAI/species etc
1237    AliDataPtr  data;
1238    Deletable   deletable;
1239    GB_ERROR    error;
1240
1241    bool has_key(const char *expected_key) const {
1242        return strcmp(GB_read_key_pntr(gb_data), expected_key) == 0;
1243    }
1244    bool has_name(const char *expected_name) const {
1245        return strcmp(item_name, expected_name) == 0;
1246    }
1247
1248    bool is_ref(TerminalType term_type) const {
1249        return
1250            type == GB_STRING &&
1251            ((term_type == IDT_SECSTRUCT && has_key("ref")) ||
1252             (term_type == IDT_SAI && has_key("_REF")));
1253    }
1254    bool is_helix(TerminalType term_type) const {
1255        return
1256            type == GB_STRING &&
1257            term_type == IDT_SAI &&
1258            (has_name("HELIX") || has_name("HELIX_NR")) &&
1259            has_key("data");
1260    }
1261
1262    bool does_allow_oversize(TerminalType term_type) const { return is_ref(term_type); }
1263    char get_std_string_gaptype(TerminalType term_type) const {
1264        bool prefers_dots = is_ref(term_type) || is_helix(term_type);
1265        return prefers_dots ? '.' : '-';
1266    }
1267
1268    AliDataPtr load_data(const SizeAwarable& oversizable, size_t size_, TerminalType term_type) {
1269        switch(type) {
1270            case GB_STRING: {
1271                const char *s = GB_read_char_pntr(gb_data);
1272                if (!s) error = GB_await_error();
1273                else {
1274                    char stdgap             = get_std_string_gaptype(term_type);
1275                    if (stdgap == '.') data = new SpecificAliData<char>(s, size_, '.', oversizable, deletable);
1276                    else data               = new SequenceAliData(s, size_, stdgap, '.', oversizable, deletable);
1277                }
1278                break;
1279            }
1280            case GB_BITS:   {
1281                const char *b    = GB_read_bits_pntr(gb_data, '-', '+');
1282                if (!b) error    = GB_await_error();
1283                else        data = new SpecificAliData<char>(b, size_, '-', oversizable, deletable);
1284                break;
1285            }
1286            case GB_BYTES:  {
1287                const char *b    = GB_read_bytes_pntr(gb_data);
1288                if (!b) error    = GB_await_error();
1289                else        data = new SpecificAliData<char>(b, size_, 0, oversizable, deletable);
1290                break;
1291            }
1292            case GB_INTS:   {
1293                const GB_UINT4 *ui   = GB_read_ints_pntr(gb_data);
1294                if (!ui) error       = GB_await_error();
1295                else            data = new SpecificAliData<GB_UINT4>(ui, size_, 0, oversizable, deletable);
1296                break;
1297            }
1298            case GB_FLOATS: {
1299                const float *f    = GB_read_floats_pntr(gb_data);
1300                if (!f) error     = GB_await_error();
1301                else         data = new SpecificAliData<float>(f, size_, 0.0, oversizable, deletable);
1302                break;
1303            }
1304
1305            default:
1306                error = GBS_global_string("Unhandled type '%i'", type);
1307                id_assert(0);
1308                break;
1309        }
1310
1311        id_assert(implicated(!error, size_ == data->elems()));
1312        return data;
1313    }
1314
1315    friend void LazyAliData::load_data() const;
1316
1317public:
1318    EditedTerminal(GBDATA *gb_data_, GB_TYPES type_, const char *item_name_, size_t size_, TerminalType term_type, const Alignment& ali, const Deletable& deletable_)
1319        : gb_data(gb_data_),
1320          type(type_),
1321          item_name(item_name_),
1322          deletable(deletable_),
1323          error(NULp)
1324    {
1325        SizeAwarable oversizable(does_allow_oversize(term_type), ali.get_len());
1326        data = new LazyAliData(oversizable, size_, term_type, *this);
1327    }
1328
1329    GB_ERROR apply(const AliEditCommand& cmd, bool& did_modify) {
1330        did_modify = false;
1331        if (!error) {
1332            AliDataPtr modified_data = cmd.apply(data, error);
1333
1334            if (!error && modified_data->differs_from(*data)) {
1335                GB_CSTR modified       = alidata2buffer(*modified_data);
1336                size_t  modified_elems = modified_data->elems();
1337
1338                switch (type) {
1339                    case GB_STRING: {
1340                        id_assert(strlen(modified) == modified_elems);
1341                        error = GB_write_string(gb_data, modified);
1342                        break;
1343                    }
1344                    case GB_BITS:   error = GB_write_bits  (gb_data, modified, modified_elems, "-");       break;
1345                    case GB_BYTES:  error = GB_write_bytes (gb_data, modified, modified_elems);            break;
1346                    case GB_INTS:   error = GB_write_ints  (gb_data, (GB_UINT4*)modified, modified_elems); break;
1347                    case GB_FLOATS: error = GB_write_floats(gb_data, (float*)modified, modified_elems);    break;
1348
1349                    default: id_assert(0); break;
1350                }
1351
1352                if (!error) did_modify = true;
1353            }
1354        }
1355        return error;
1356    }
1357};
1358
1359void LazyAliData::load_data() const {
1360    loaded = terminal.load_data(*this, elems(), term_type);
1361}
1362
1363GB_ERROR AliEditor::apply_to_terminal(GBDATA *gb_data, TerminalType term_type, const char *item_name, const Alignment& ali) const {
1364    GB_TYPES gbtype  = GB_read_type(gb_data);
1365    GB_ERROR error = NULp;
1366    if (gbtype >= GB_BITS && gbtype != GB_OBSOLETE) {
1367        if (shall_edit(gb_data, term_type)) {
1368            EditedTerminal edited(gb_data, gbtype, item_name, GB_read_count(gb_data), term_type, ali, deletable);
1369
1370            bool terminal_was_modified;
1371            error = edited.apply(edit_command(), terminal_was_modified);
1372            if (terminal_was_modified) {
1373                progress.subtitle(GBS_global_string("modified: %zu", ++modified_counter));
1374            }
1375        }
1376    }
1377    progress.inc_and_check_user_abort(error);
1378    return error;
1379}
1380
1381// --------------------------------------------------------------------------------
1382
1383static size_t countAffectedEntries(GBDATA *Main, const Alignment& ali) {
1384    AliEntryCounter counter;
1385    counter.apply_to_alignment(Main, ali);
1386    return counter.get_entry_count();
1387}
1388
1389static GB_ERROR apply_command_to_alignment(const AliEditCommand& cmd, const char *cmd_description, GBDATA *Main, const char *alignment_name, const char *deletable_chars) {
1390    // applies 'cmd' to one or all alignments
1391    // (if 'alignment_name' is NULp, all alignments are affected - probably useless case)
1392    //
1393    // 'deletable_chars' is either
1394    // - NULp -> nothing may be deleted
1395    // - "%" -> anything may be deleted
1396    // - or a string containing all deletable characters
1397
1398    Deletable deletable =
1399        deletable_chars
1400        ? ( strchr(deletable_chars, '%')
1401            ? Deletable(Deletable::ANYTHING)
1402            : Deletable(deletable_chars))
1403        : Deletable(Deletable::NOTHING);
1404
1405    GB_ERROR  error      = NULp;
1406    GBDATA   *gb_presets = GBT_get_presets(Main);
1407
1408    for (GBDATA *gb_ali = GB_entry(gb_presets, "alignment");
1409         gb_ali && !error;
1410         gb_ali = GB_nextEntry(gb_ali))
1411    {
1412        GBDATA *gb_name = GB_find_string(gb_ali, "alignment_name", alignment_name, GB_IGNORE_CASE, SEARCH_CHILD);
1413
1414        if (gb_name) {
1415            GBDATA    *gb_len = GB_entry(gb_ali, "alignment_len");
1416            Alignment  ali(GB_read_char_pntr(gb_name), GB_read_int(gb_len));
1417
1418            size_t resulting_ali_length;
1419            error = cmd.check_applicable_to(ali, resulting_ali_length);
1420
1421            if (!error) error = AliEditor(cmd, deletable, cmd_description, countAffectedEntries(Main, ali)).apply_to_alignment(Main, ali);
1422            if (!error) error = GB_write_int(gb_len, resulting_ali_length);
1423        }
1424    }
1425
1426    free_insDelBuffer();
1427
1428    if (!error) GB_disable_quicksave(Main, "a lot of sequences changed"); // @@@ only disable if a reasonable amount of sequences has changed!
1429
1430    return error;
1431}
1432
1433static GB_ERROR format_to_alilen(GBDATA *Main, const char *alignment_name) { // @@@ inline
1434    AliAutoFormatCommand fcmd;
1435    return apply_command_to_alignment(fcmd, "Formatting alignment", Main, alignment_name, "-.");
1436}
1437
1438GB_ERROR ARB_format_alignment(GBDATA *Main, const char *alignment_name) {
1439    GB_ERROR err = NULp;
1440
1441    if (strcmp(alignment_name, GENOM_ALIGNMENT) != 0) {         // NEVER EVER format 'ali_genom'
1442        err           = GBT_check_data(Main, alignment_name);   // detect max. length
1443        if (!err) err = format_to_alilen(Main, alignment_name); // format sequences in alignment
1444        if (!err) err = GBT_check_data(Main, alignment_name);   // sets state to "formatted"
1445    }
1446    else {
1447        err = "It's forbidden to format '" GENOM_ALIGNMENT "'!";
1448    }
1449    return err;
1450}
1451
1452GB_ERROR ARB_insdel_columns(GBDATA *Main, const char *alignment_name, long pos, long count, const char *deletable_chars) {
1453    /* if count > 0     insert 'count' characters at pos
1454     * if count < 0     delete pos to pos+|count|
1455     *
1456     * Note: deleting is only performed, if found characters in deleted range are listed in 'deletable_chars'
1457     *       otherwise function returns with an error.
1458     *       (if 'deletable_chars' contains a '%', any character will be deleted)
1459     *
1460     * This affects all species' and SAIs having data in given 'alignment_name' and
1461     * modifies several data entries found there
1462     * (see shall_edit() for details which fields are affected).
1463     */
1464
1465    GB_ERROR error = NULp;
1466
1467    if (pos<0) {
1468        error = GBS_global_string("Illegal sequence position %li", pos);
1469    }
1470    else {
1471        const char *description = NULp;
1472
1473        SmartPtr<AliEditCommand> idcmd;
1474        if (count<0) {
1475            idcmd       = new AliDeleteCommand(pos, -count);
1476            description = "Deleting columns";
1477        }
1478        else {
1479            idcmd = new AliInsertCommand(pos, count);
1480            description = "Inserting columns";
1481        }
1482
1483        error = apply_command_to_alignment(*idcmd, description, Main, alignment_name, deletable_chars);
1484    }
1485    return error;
1486}
1487
1488// AISC_MKPT_PROMOTE:class RangeList;
1489// AISC_MKPT_PROMOTE:enum UseRange { RANGES, SINGLE_COLUMNS };
1490// AISC_MKPT_PROMOTE:enum InsertWhere { INFRONTOF, BEHIND };
1491
1492GB_ERROR ARB_delete_columns_using_SAI(GBDATA *Main, const char *alignment_name, const RangeList& ranges, const char *deletable_chars) {
1493    // Deletes all columns defined by 'ranges'
1494    // from all members (SAIs, seqs, ..) of alignment named 'alignment_name'.
1495
1496    GB_ERROR error;
1497    if (ranges.empty()) {
1498        error = "Done with deleting nothing :)";
1499    }
1500    else {
1501        AliEditCommand *cmd = new AliAutoFormatCommand; // @@@ use SmartPtr (here and in AliCompositeCommand)
1502        for (RangeList::reverse_iterator r = ranges.rbegin(); r != ranges.rend(); ++r) {
1503            cmd = new AliCompositeCommand(cmd, new AliDeleteCommand(r->start(), r->size()));
1504        }
1505        error = apply_command_to_alignment(*cmd, "Deleting columns using SAI", Main, alignment_name, deletable_chars);
1506        delete cmd;
1507    }
1508    return error;
1509}
1510
1511GB_ERROR ARB_insert_columns_using_SAI(GBDATA *Main, const char *alignment_name, const RangeList& ranges, UseRange units, InsertWhere where, size_t amount) {
1512    // Insert 'amount' columns into all members of the alignment named 'alignment_name'.
1513    //
1514    // If units is
1515    // - RANGES, each range
1516    // - SINGLE_COLUMNS, each column of each range
1517    // is handled as a unit.
1518    //
1519    // InsertWhere specifies whether the insertion happens INFRONTOF or BEHIND
1520
1521    GB_ERROR error;
1522    if (!amount || ranges.empty()) {
1523        error = "Done with inserting no gaps :)";
1524    }
1525    else {
1526        AliEditCommand *cmd = new AliAutoFormatCommand; // @@@ use SmartPtr (here and in AliCompositeCommand)
1527        for (RangeList::reverse_iterator r = ranges.rbegin(); r != ranges.rend(); ++r) {
1528            switch (units) {
1529                case RANGES: {
1530                    int pos = 0;
1531                    switch (where) {
1532                        case INFRONTOF: pos = r->start(); break;
1533                        case BEHIND:    pos = r->end()+1; break;
1534                    }
1535                    cmd = new AliCompositeCommand(cmd, new AliInsertCommand(pos, amount));
1536                    break;
1537                }
1538                case SINGLE_COLUMNS: {
1539                    for (int pos = r->end(); pos >= r->start(); --pos)  {
1540                        cmd = new AliCompositeCommand(cmd, new AliInsertCommand(where == INFRONTOF ? pos : pos+1, amount));
1541                    }
1542                    break;
1543                }
1544            }
1545        }
1546        error = apply_command_to_alignment(*cmd, "Inserting columns using SAI", Main, alignment_name, NULp);
1547        delete cmd;
1548    }
1549    return error;
1550}
1551
1552// --------------------------------------------------------------------------------
1553
1554#ifdef UNIT_TESTS
1555#ifndef TEST_UNIT_H
1556#include <test_unit.h>
1557#endif
1558#include <arb_unit_test.h>
1559
1560#define PLAIN_APPLY_CMD(str,cmd)                                                                                        \
1561    size_t     str_len = strlen(str);                                                                                   \
1562    AliDataPtr data    = new SequenceAliData(str, str_len, '-', '.', dontAllowOversize(str_len), Deletable("-."));      \
1563    GB_ERROR   error   = NULp;                                                                                          \
1564    AliDataPtr mod     = cmd.apply(data, error)
1565
1566#define APPLY_CMD(str,cmd)                                                      \
1567    PLAIN_APPLY_CMD(str, cmd);                                                  \
1568    TEST_EXPECT_NO_ERROR(error);                                                \
1569    GB_CSTR    res     = mod->differs_from(*data) ? alidata2buffer(*mod) : NULp
1570
1571#define DO_FORMAT(str,wanted_len)       \
1572    AliFormatCommand cmd(wanted_len);   \
1573    APPLY_CMD(str, cmd)
1574
1575#define DO_INSERT(str,pos,amount)       \
1576    AliInsertCommand cmd(pos, amount);  \
1577    APPLY_CMD(str, cmd)
1578
1579#define DO_FORMAT_AND_INSERT(str,wanted_len,pos,amount)         \
1580    AliCompositeCommand cmd(new AliFormatCommand(wanted_len),   \
1581                            new AliInsertCommand(pos,amount));  \
1582    APPLY_CMD(str, cmd)
1583
1584#define DO_DELETE(str,pos,amount)       \
1585    AliDeleteCommand cmd(pos, amount);  \
1586    APPLY_CMD(str, cmd)
1587
1588#define TEST_FORMAT(str,wanted_alilen,expected)         do { DO_FORMAT(str,wanted_alilen); TEST_EXPECT_EQUAL(res, expected);         } while(0)
1589#define TEST_FORMAT__BROKEN(str,wanted_alilen,expected) do { DO_FORMAT(str,wanted_alilen); TEST_EXPECT_EQUAL__BROKEN(res, expected); } while(0)
1590
1591#define TEST_INSERT(str,pos,amount,expected)         do { DO_INSERT(str,pos,amount); TEST_EXPECT_EQUAL(res, expected);         } while(0)
1592#define TEST_INSERT__BROKEN(str,pos,amount,expected) do { DO_INSERT(str,pos,amount); TEST_EXPECT_EQUAL__BROKEN(res, expected); } while(0)
1593
1594#define TEST_DELETE(str,pos,amount,expected)         do { DO_DELETE(str,pos,amount); TEST_EXPECT_EQUAL(res, expected);         } while(0)
1595#define TEST_DELETE__BROKEN(str,pos,amount,expected) do { DO_DELETE(str,pos,amount); TEST_EXPECT_EQUAL__BROKEN(res, expected); } while(0)
1596
1597#define TEST_FORMAT_AND_INSERT(str,wanted_alilen,pos,amount,expected)         do { DO_FORMAT_AND_INSERT(str,wanted_alilen,pos,amount); TEST_EXPECT_EQUAL(res, expected);         } while(0)
1598#define TEST_FORMAT_AND_INSERT__BROKEN(str,wanted_alilen,pos,amount,expected) do { DO_FORMAT_AND_INSERT(str,wanted_alilen,pos,amount); TEST_EXPECT_EQUAL__BROKEN(res, expected); } while(0)
1599
1600#define TEST_FORMAT_ERROR(str,wanted_alilen,exp_err) do {        \
1601        AliFormatCommand cmd(wanted_alilen);                     \
1602        PLAIN_APPLY_CMD(str, cmd);                               \
1603        TEST_EXPECT_ERROR_CONTAINS(error, exp_err);              \
1604    } while(0)
1605
1606#define TEST_DELETE_ERROR(str,pos,amount,exp_err) do {           \
1607        AliDeleteCommand cmd(pos, amount);                       \
1608        PLAIN_APPLY_CMD(str, cmd);                               \
1609        TEST_EXPECT_ERROR_CONTAINS(error, exp_err);              \
1610    } while(0)
1611
1612
1613// --------------------------------------------------------------------------------
1614
1615void TEST_format_insert_delete() {
1616    // this test is a bit weird.
1617    //
1618    // originally it was used to test the function gbt_insert_delete, which is gone now.
1619    // now it tests AliFormatCommand, AliInsertCommand, AliDeleteCommand and AliCompositeCommand (but quite implicit).
1620
1621    const char *UNMODIFIED = NULp;
1622
1623    TEST_FORMAT("xxx",   5, "xxx..");
1624    TEST_FORMAT(".x.",   5, ".x...");
1625    TEST_FORMAT(".x..",  5, ".x...");
1626    TEST_FORMAT(".x...", 5, UNMODIFIED);
1627
1628    TEST_FORMAT("xxx--", 3, "xxx");
1629    TEST_FORMAT("xxx..", 3, "xxx");
1630    TEST_FORMAT_ERROR("xxxxx", 3, "You tried to delete 'x' at position 3  -> Operation aborted");
1631    TEST_FORMAT_ERROR("xxx",   0, "You tried to delete 'x' at position 0  -> Operation aborted");
1632
1633    // insert/delete in the middle
1634    TEST_INSERT("abcde", 3, 0, UNMODIFIED);
1635    TEST_INSERT("abcde", 3, 1, "abc-de");
1636    TEST_INSERT("abcde", 3, 2, "abc--de");
1637
1638    TEST_DELETE("abcde",   3, 0, UNMODIFIED);
1639    TEST_DELETE("abc-de",  3, 1, "abcde");
1640    TEST_DELETE("abc--de", 3, 2, "abcde");
1641    TEST_DELETE_ERROR("abc-xde", 3, 2, "You tried to delete 'x' at position 4  -> Operation aborted");
1642
1643    // insert/delete at end
1644    TEST_INSERT("abcde", 5, 1, "abcde.");
1645    TEST_INSERT("abcde", 5, 4, "abcde....");
1646
1647    TEST_DELETE("abcde-",    5, 1, "abcde");
1648    TEST_DELETE("abcde----", 5, 4, "abcde");
1649
1650    // insert/delete at start
1651    TEST_INSERT("abcde", 0, 1, ".abcde");
1652    TEST_INSERT("abcde", 0, 4, "....abcde");
1653
1654    TEST_DELETE("-abcde",    0, 1, "abcde");
1655    TEST_DELETE("----abcde", 0, 4, "abcde");
1656
1657    // insert behind end
1658    TEST_FORMAT_AND_INSERT("abcde", 10, 8, 1, "abcde......");
1659    TEST_FORMAT_AND_INSERT("abcde", 10, 8, 4, "abcde.........");
1660
1661    // insert/delete all
1662    TEST_INSERT("",    0, 3, "...");
1663    TEST_DELETE("---", 0, 3, "");
1664
1665    free_insDelBuffer();
1666}
1667
1668// ------------------------------
1669
1670static struct arb_unit_test::test_alignment_data TADinsdel[] = {
1671    { 1, "MtnK1722", "...G-GGC-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUCACCUCC....." },
1672    { 1, "MhnFormi", "---A-CGA-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUCACCUCCU....." },
1673    { 1, "MhnT1916", "...A-CGA-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUCACCUCCU----" },
1674};
1675
1676static struct arb_unit_test::test_alignment_data EXTinsdel[] = {
1677    { 0, "ECOLI",    "---U-GCC-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCCCACCUGA...." },
1678    { 0, "HELIX",    ".....[<[.........[..[..[<<.[..].>>]....]..]....].>......]" },
1679    { 0, "HELIX_NR", ".....1.1.........25.25.34..34.34..34...25.25...1........1" },
1680};
1681
1682#define HELIX_REF    ".....x..x........x...x.x....x.x....x...x...x...x.........x"
1683#define HELIX_STRUCT "VERSION=3\nLOOP={etc.pp\n}\n"
1684
1685static const char *read_item_entry(GBDATA *gb_item, const char *ali_name, const char *entry_name) {
1686    const char *result = NULp;
1687    if (gb_item) {
1688        GBDATA *gb_ali = GB_find(gb_item, ali_name, SEARCH_CHILD);
1689        if (gb_ali) {
1690            GBDATA *gb_entry = GB_entry(gb_ali, entry_name);
1691            if (gb_entry) {
1692                result = GB_read_char_pntr(gb_entry);
1693            }
1694        }
1695    }
1696    if (!result) TEST_EXPECT_NO_ERROR(GB_await_error());
1697    return result;
1698}
1699static char *ints2string(const GB_UINT4 *ints, size_t count) {
1700    char *str = ARB_alloc<char>(count+1);
1701    for (size_t c = 0; c<count; ++c) { // IRRELEVANT_LOOP
1702        str[c] = (ints[c]<10) ? ints[c]+'0' : '?';
1703    }
1704    str[count] = 0;
1705    return str;
1706}
1707static GB_UINT4 *string2ints(const char *str, size_t count) {
1708    GB_UINT4 *ints = ARB_alloc<GB_UINT4>(count);
1709    for (size_t c = 0; c<count; ++c) { // IRRELEVANT_LOOP
1710        ints[c] = int(str[c]-'0');
1711    }
1712    return ints;
1713}
1714static char *floats2string(const float *floats, size_t count) {
1715    char *str = ARB_alloc<char>(count+1);
1716    for (size_t c = 0; c<count; ++c) { // IRRELEVANT_LOOP
1717        str[c] = char(floats[c]*64.0+0.5)+' '+1;
1718    }
1719    str[count] = 0;
1720    return str;
1721}
1722static float *string2floats(const char *str, size_t count) {
1723    float *floats = ARB_alloc<float>(count);
1724    for (size_t c = 0; c<count; ++c) { // IRRELEVANT_LOOP
1725        floats[c] = float(str[c]-' '-1)/64.0;
1726    }
1727    return floats;
1728}
1729
1730static GBDATA *get_ali_entry(GBDATA *gb_item, const char *ali_name, const char *entry_name) {
1731    GBDATA *gb_entry = NULp;
1732    if (gb_item) {
1733        GBDATA *gb_ali = GB_find(gb_item, ali_name, SEARCH_CHILD);
1734        if (gb_ali) gb_entry = GB_entry(gb_ali, entry_name);
1735    }
1736    return gb_entry;
1737}
1738
1739static char *read_item_ints_entry_as_string(GBDATA *gb_item, const char *ali_name, const char *entry_name) {
1740    char   *result   = NULp;
1741    GBDATA *gb_entry = get_ali_entry(gb_item, ali_name, entry_name);
1742    if (gb_entry) {
1743        GB_UINT4 *ints = GB_read_ints(gb_entry);
1744        result         = ints2string(ints, GB_read_count(gb_entry));
1745        free(ints);
1746    }
1747    if (!result) TEST_EXPECT_NO_ERROR(GB_await_error());
1748    return result;
1749}
1750static char *read_item_floats_entry_as_string(GBDATA *gb_item, const char *ali_name, const char *entry_name) {
1751    char   *result   = NULp;
1752    GBDATA *gb_entry = get_ali_entry(gb_item, ali_name, entry_name);
1753    if (gb_entry) {
1754        float *floats = GB_read_floats(gb_entry);
1755        result         = floats2string(floats, GB_read_count(gb_entry));
1756        free(floats);
1757    }
1758    if (!result) TEST_EXPECT_NO_ERROR(GB_await_error());
1759    return result;
1760}
1761
1762#define TEST_ITEM_HAS_ENTRY(find,name,ename,expected)                                   \
1763    TEST_EXPECT_EQUAL(read_item_entry(find(gb_main, name), ali_name, ename), expected)
1764
1765#define TEST_ITEM_HAS_INTSENTRY(find,name,ename,expected)                                                               \
1766    TEST_EXPECT_EQUAL(&*SmartCharPtr(read_item_ints_entry_as_string(find(gb_main, name), ali_name, ename)), expected)
1767
1768#define TEST_ITEM_HAS_FLOATSENTRY(find,name,ename,expected)                                                             \
1769    TEST_EXPECT_EQUAL(&*SmartCharPtr(read_item_floats_entry_as_string(find(gb_main, name), ali_name, ename)), expected)
1770
1771#define TEST_ITEM_HAS_DATA(find,name,expected) TEST_ITEM_HAS_ENTRY(find,name,"data",expected)
1772
1773#define TEST_SPECIES_HAS_DATA(ad,sd)    TEST_ITEM_HAS_DATA(GBT_find_species,ad.name,sd)
1774#define TEST_SAI_HAS_DATA(ad,sd)        TEST_ITEM_HAS_DATA(GBT_find_SAI,ad.name,sd)
1775#define TEST_SAI_HAS_ENTRY(ad,ename,sd) TEST_ITEM_HAS_ENTRY(GBT_find_SAI,ad.name,ename,sd)
1776
1777#define TEST_SPECIES_HAS_INTS(ad,id)   TEST_ITEM_HAS_INTSENTRY(GBT_find_species,ad.name,"NN",id)
1778#define TEST_SPECIES_HAS_FLOATS(ad,fd) TEST_ITEM_HAS_FLOATSENTRY(GBT_find_species,ad.name,"FF",fd)
1779
1780#define TEST_DATA(sd0,sd1,sd2,ed0,ed1,ed2,ref,ints,floats,struct) do {                          \
1781        TEST_SPECIES_HAS_DATA(TADinsdel[0], sd0);                                               \
1782        TEST_SPECIES_HAS_DATA(TADinsdel[1], sd1);                                               \
1783        TEST_SPECIES_HAS_DATA(TADinsdel[2], sd2);                                               \
1784        TEST_SAI_HAS_DATA(EXTinsdel[0], ed0);                                                   \
1785        TEST_SAI_HAS_DATA(EXTinsdel[1], ed1);                                                   \
1786        TEST_SAI_HAS_DATA(EXTinsdel[2], ed2);                                                   \
1787        TEST_SAI_HAS_ENTRY(EXTinsdel[1], "_REF", ref);                                          \
1788        GBDATA *gb_ref = GB_search(gb_main, "secedit/structs/ali_mini/struct/ref", GB_FIND);    \
1789        TEST_EXPECT_EQUAL(GB_read_char_pntr(gb_ref), ref);                                      \
1790        TEST_SPECIES_HAS_INTS(TADinsdel[0], ints);                                              \
1791        TEST_SPECIES_HAS_FLOATS(TADinsdel[0], floats);                                          \
1792        TEST_SAI_HAS_ENTRY(EXTinsdel[1], "_STRUCT", struct);                                    \
1793    } while(0)
1794
1795static int get_alignment_aligned(GBDATA *gb_main, const char *aliname) { // former GBT_get_alignment_aligned
1796    GBDATA *gb_alignment = GBT_get_alignment(gb_main, aliname);
1797    return gb_alignment ? *GBT_read_int(gb_alignment, "aligned") : -1;
1798}
1799
1800#define TEST_ALI_LEN_ALIGNED(len,aligned) do {                                  \
1801        TEST_EXPECT_EQUAL(GBT_get_alignment_len(gb_main, ali_name), len);       \
1802        TEST_EXPECT_EQUAL(get_alignment_aligned(gb_main, ali_name), aligned);   \
1803    } while(0)
1804
1805static ARB_ERROR add_some_SAIs(GBDATA *gb_main, const char *ali_name) {
1806    ARB_ERROR      error;
1807    GB_transaction ta(gb_main);
1808    TEST_DB_INSERT_SAI(gb_main, error, ali_name, EXTinsdel);
1809
1810    // add secondary structure to "HELIX"
1811    GBDATA *gb_helix     = GBT_find_SAI(gb_main, "HELIX");
1812    if (!gb_helix) error = GB_await_error();
1813    else {
1814        GBDATA *gb_struct     = GBT_add_data(gb_helix, ali_name, "_STRUCT", GB_STRING);
1815        if (!gb_struct) error = GB_await_error();
1816        else    error         = GB_write_string(gb_struct, HELIX_STRUCT);
1817
1818        GBDATA *gb_struct_ref     = GBT_add_data(gb_helix, ali_name, "_REF", GB_STRING);
1819        if (!gb_struct_ref) error = GB_await_error();
1820        else    error             = GB_write_string(gb_struct_ref, HELIX_REF);
1821    }
1822
1823    // add stored secondary structure
1824    GBDATA *gb_ref     = GB_search(gb_main, "secedit/structs/ali_mini/struct/ref", GB_STRING);
1825    if (!gb_ref) error = GB_await_error();
1826    else    error      = GB_write_string(gb_ref, HELIX_REF);
1827
1828    // create one INTS and one FLOATS entry for first species
1829    GBDATA *gb_spec = GBT_find_species(gb_main, TADinsdel[0].name);
1830    {
1831        GBDATA     *gb_ints   = GBT_add_data(gb_spec, ali_name, "NN", GB_INTS);
1832        const char *intsAsStr = "9346740960354855652100942568200611650200211394358998513";
1833        size_t      len       = strlen(intsAsStr);
1834        GB_UINT4   *ints      = string2ints(intsAsStr, len);
1835        {
1836            char *asStr = ints2string(ints, len);
1837            TEST_EXPECT_EQUAL(intsAsStr, asStr);
1838            free(asStr);
1839        }
1840        error = GB_write_ints(gb_ints, ints, len);
1841        free(ints);
1842    }
1843    {
1844        GBDATA     *gb_ints     = GBT_add_data(gb_spec, ali_name, "FF", GB_FLOATS);
1845        const char *floatsAsStr = "ODu8EJh60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uCLDoHlWV59DW";
1846        size_t      len         = strlen(floatsAsStr);
1847        float      *floats      = string2floats(floatsAsStr, len);
1848        {
1849            char *asStr = floats2string(floats, len);
1850            TEST_EXPECT_EQUAL(floatsAsStr, asStr);
1851            free(asStr);
1852        }
1853        error = GB_write_floats(gb_ints, floats, len);
1854        free(floats);
1855    }
1856    return error;
1857}
1858
1859__ATTR__REDUCED_OPTIMIZE__NO_GCSE static void test_insert_delete_DB() {
1860    GB_shell    shell;
1861    ARB_ERROR   error;
1862    const char *ali_name = "ali_mini";
1863    GBDATA     *gb_main  = TEST_CREATE_DB(error, ali_name, TADinsdel, false);
1864
1865    arb_suppress_progress noProgress;
1866
1867    if (!error) error = add_some_SAIs(gb_main, ali_name);
1868    if (!error) {
1869        GB_transaction ta(gb_main);
1870
1871        for (int pass = 1; pass <= 2; ++pass) {
1872            if (pass == 1) TEST_ALI_LEN_ALIGNED(56, 1);
1873            if (pass == 2) TEST_ALI_LEN_ALIGNED(57, 0); // was marked as "not aligned"
1874
1875            TEST_DATA("...G-GGC-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUCACCUCC.....",
1876                      "---A-CGA-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUCACCUCCU.....",
1877                      "...A-CGA-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUCACCUCCU----",
1878                      "---U-GCC-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCCCACCUGA....",
1879                      ".....[<[.........[..[..[<<.[..].>>]....]..]....].>......]",
1880                      ".....1.1.........25.25.34..34.34..34...25.25...1........1",
1881                      ".....x..x........x...x.x....x.x....x...x...x...x.........x",
1882                      "9346740960354855652100942568200611650200211394358998513",  // a INTS entry
1883                      "ODu8EJh60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uCLDoHlWV59DW", // a FLOATS entry
1884                      HELIX_STRUCT);
1885
1886            if (pass == 1) TEST_EXPECT_NO_ERROR(GBT_check_data(gb_main, ali_name));
1887        }
1888
1889        TEST_EXPECT_NO_ERROR(ARB_format_alignment(gb_main, ali_name));
1890        TEST_ALI_LEN_ALIGNED(57, 1);
1891        TEST_DATA("...G-GGC-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUCACCUCC......",
1892                  "---A-CGA-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUCACCUCCU.....",
1893                  "...A-CGA-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUCACCUCCU-----", // @@@ <- should convert '-' to '.'
1894                  "---U-GCC-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCCCACCUGA.....",
1895                  ".....[<[.........[..[..[<<.[..].>>]....]..]....].>......]",
1896                  ".....1.1.........25.25.34..34.34..34...25.25...1........1",
1897                  ".....x..x........x...x.x....x.x....x...x...x...x.........x",
1898                  "934674096035485565210094256820061165020021139435899851300",
1899                  "ODu8EJh60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uCLDoHlWV59DW!",
1900                  HELIX_STRUCT);
1901
1902// text-editor column -> alignment column
1903#define COL(col) ((col)-19)
1904
1905        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(64), 2, "")); // insert in middle
1906        TEST_ALI_LEN_ALIGNED(59, 1);
1907        TEST_DATA("...G-GGC-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCC......",
1908                  "---A-CGA-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU.....",
1909                  "...A-CGA-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU-----",
1910                  "---U-GCC-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCC--CACCUGA.....",
1911                  ".....[<[.........[..[..[<<.[..].>>]....]..]......].>......]",
1912                  ".....1.1.........25.25.34..34.34..34...25.25.....1........1",
1913                  ".....x..x........x...x.x....x.x....x...x...x.....x.........x",
1914                  "93467409603548556521009425682006116502002113900435899851300",
1915                  "ODu8EJh60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uC!!LDoHlWV59DW!",
1916                  HELIX_STRUCT);
1917
1918        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(75), 2, "")); // insert near end
1919        TEST_ALI_LEN_ALIGNED(61, 1);
1920        TEST_DATA("...G-GGC-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCC........",
1921                  "---A-CGA-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU.......",
1922                  "...A-CGA-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU-------",
1923                  "---U-GCC-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCC--CACCUGA.......",
1924                  ".....[<[.........[..[..[<<.[..].>>]....]..]......].>........]",
1925                  ".....1.1.........25.25.34..34.34..34...25.25.....1..........1",
1926                  ".....x..x........x...x.x....x.x....x...x...x.....x...........x",
1927                  "9346740960354855652100942568200611650200211390043589985100300",
1928                  "ODu8EJh60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uC!!LDoHlWV59!!DW!",
1929                  HELIX_STRUCT);
1930
1931        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(20), 2, "")); // insert near start
1932        TEST_ALI_LEN_ALIGNED(63, 1);
1933        TEST_DATA(".....G-GGC-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCC........",
1934                  "-----A-CGA-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU.......",
1935                  ".....A-CGA-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU-------",
1936                  "-----U-GCC-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCC--CACCUGA.......",
1937                  ".......[<[.........[..[..[<<.[..].>>]....]..]......].>........]",
1938                  ".......1.1.........25.25.34..34.34..34...25.25.....1..........1",
1939                  ".......x..x........x...x.x....x.x....x...x...x.....x...........x",
1940                  "900346740960354855652100942568200611650200211390043589985100300",
1941                  "O!!Du8EJh60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uC!!LDoHlWV59!!DW!",
1942                  HELIX_STRUCT);
1943
1944
1945        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(26), 2, "")); // insert at left helix start
1946        TEST_ALI_LEN_ALIGNED(65, 1);
1947        TEST_DATA(".....G---GGC-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCC........",
1948                  "-----A---CGA-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU.......",
1949                  ".....A---CGA-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU-------",
1950                  "-----U---GCC-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCC--CACCUGA.......",
1951                  ".........[<[.........[..[..[<<.[..].>>]....]..]......].>........]",
1952                  ".........1.1.........25.25.34..34.34..34...25.25.....1..........1",
1953                  ".........x..x........x...x.x....x.x....x...x...x.....x...........x",
1954                  "90034670040960354855652100942568200611650200211390043589985100300",
1955                  "O!!Du8E!!Jh60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uC!!LDoHlWV59!!DW!",
1956                  HELIX_STRUCT);
1957
1958        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(29), 2, "")); // insert behind left helix start
1959        TEST_ALI_LEN_ALIGNED(67, 1);
1960        TEST_DATA(".....G---G--GC-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCC........",
1961                  "-----A---C--GA-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU.......",
1962                  ".....A---C--GA-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU-------",
1963                  "-----U---G--CC-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCC--CACCUGA.......",
1964                  ".........[..<[.........[..[..[<<.[..].>>]....]..]......].>........]",
1965                  ".........1...1.........25.25.34..34.34..34...25.25.....1..........1",
1966                  ".........x....x........x...x.x....x.x....x...x...x.....x...........x",
1967                  "9003467004000960354855652100942568200611650200211390043589985100300",
1968                  "O!!Du8E!!J!!h60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uC!!LDoHlWV59!!DW!",
1969                  HELIX_STRUCT);
1970
1971        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(32), 2, "")); // insert at left helix end
1972        TEST_ALI_LEN_ALIGNED(69, 1);
1973        TEST_DATA(".....G---G--G--C-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCC........",
1974                  "-----A---C--G--A-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU.......",
1975                  ".....A---C--G--A-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU-------",
1976                  "-----U---G--C--C-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCC--CACCUGA.......",
1977                  ".........[..<..[.........[..[..[<<.[..].>>]....]..]......].>........]",
1978                  ".........1.....1.........25.25.34..34.34..34...25.25.....1..........1",
1979                  ".........x......x........x...x.x....x.x....x...x...x.....x...........x",
1980                  "900346700400000960354855652100942568200611650200211390043589985100300",
1981                  "O!!Du8E!!J!!h!!60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uC!!LDoHlWV59!!DW!",
1982                  HELIX_STRUCT);
1983
1984        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(35), 2, ""));    // insert behind left helix end
1985        TEST_ALI_LEN_ALIGNED(71, 1);
1986        TEST_DATA(".....G---G--G--C---C-G...--A--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCC........",
1987                  "-----A---C--G--A---U-C-----C--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU.......",
1988                  ".....A---C--G--A---A-C.....G--G--GAA-CCUG-CGGC-UGG--AUC--ACCUCCU-------",
1989                  "-----U---G--C--C---U-G-----G--C--CCU-UAGC-GCGG-UGG--UCC--CACCUGA.......",
1990                  ".........[..<..[...........[..[..[<<.[..].>>]....]..]......].>........]",
1991                  ".........1.....1...........25.25.34..34.34..34...25.25.....1..........1",
1992                  ".........x........x........x...x.x....x.x....x...x...x.....x...........x", // @@@ _REF gets destroyed here! (see #159)
1993                  //               ^ ^
1994                  "90034670040000090060354855652100942568200611650200211390043589985100300",
1995                  "O!!Du8E!!J!!h!!6!!0e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uC!!LDoHlWV59!!DW!",
1996                  HELIX_STRUCT);
1997
1998
1999        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(59), 2, "")); // insert at right helix start
2000        TEST_ALI_LEN_ALIGNED(73, 1);
2001        TEST_DATA(".....G---G--G--C---C-G...--A--G--GAA-CCU--G-CGGC-UGG--AUC--ACCUCC........",
2002                  "-----A---C--G--A---U-C-----C--G--GAA-CCU--G-CGGC-UGG--AUC--ACCUCCU.......",
2003                  ".....A---C--G--A---A-C.....G--G--GAA-CCU--G-CGGC-UGG--AUC--ACCUCCU-------",
2004                  "-----U---G--C--C---U-G-----G--C--CCU-UAG--C-GCGG-UGG--UCC--CACCUGA.......",
2005                  ".........[..<..[...........[..[..[<<.[....].>>]....]..]......].>........]",
2006                  ".........1.....1...........25.25.34..34...34..34...25.25.....1..........1",
2007                  ".........x........x........x...x.x....x...x....x...x...x.....x...........x",
2008                  "9003467004000009006035485565210094256820000611650200211390043589985100300",
2009                  "O!!Du8E!!J!!h!!6!!0e1XYLgxvzrqmeMiMAjB5E!!JxT6JPiCvQrq4uC!!LDoHlWV59!!DW!",
2010                  HELIX_STRUCT);
2011
2012        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(62), 2, ""));       // insert behind right helix start
2013        TEST_ALI_LEN_ALIGNED(75, 1);
2014        TEST_DATA(".....G---G--G--C---C-G...--A--G--GAA-CCU--G---CGGC-UGG--AUC--ACCUCC........",
2015                  "-----A---C--G--A---U-C-----C--G--GAA-CCU--G---CGGC-UGG--AUC--ACCUCCU.......",
2016                  ".....A---C--G--A---A-C.....G--G--GAA-CCU--G---CGGC-UGG--AUC--ACCUCCU-------",
2017                  "-----U---G--C--C---U-G-----G--C--CCU-UAG--C---GCGG-UGG--UCC--CACCUGA.......",
2018                  ".........[..<..[...........[..[..[<<.[....]...>>]....]..]......].>........]",
2019                  ".........1.....1...........25.25.34..34...3..4..34...25.25.....1..........1", // @@@ <- helix nr destroyed
2020                  //                                         ^^^^
2021                  ".........x........x........x...x.x....x...x......x...x...x.....x...........x",
2022                  "900346700400000900603548556521009425682000000611650200211390043589985100300",
2023                  "O!!Du8E!!J!!h!!6!!0e1XYLgxvzrqmeMiMAjB5E!!J!!xT6JPiCvQrq4uC!!LDoHlWV59!!DW!",
2024                  HELIX_STRUCT);
2025
2026        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(67), 2, ""));         // insert at right helix end
2027        TEST_ALI_LEN_ALIGNED(77, 1);
2028        TEST_DATA(".....G---G--G--C---C-G...--A--G--GAA-CCU--G---CG--GC-UGG--AUC--ACCUCC........",
2029                  "-----A---C--G--A---U-C-----C--G--GAA-CCU--G---CG--GC-UGG--AUC--ACCUCCU.......",
2030                  ".....A---C--G--A---A-C.....G--G--GAA-CCU--G---CG--GC-UGG--AUC--ACCUCCU-------",
2031                  "-----U---G--C--C---U-G-----G--C--CCU-UAG--C---GC--GG-UGG--UCC--CACCUGA.......",
2032                  ".........[..<..[...........[..[..[<<.[....]...>>..]....]..]......].>........]",
2033                  ".........1.....1...........25.25.34..34...3..4....34...25.25.....1..........1",
2034                  ".........x........x........x...x.x....x...x........x...x...x.....x...........x",
2035                  "90034670040000090060354855652100942568200000061100650200211390043589985100300",
2036                  "O!!Du8E!!J!!h!!6!!0e1XYLgxvzrqmeMiMAjB5E!!J!!xT6!!JPiCvQrq4uC!!LDoHlWV59!!DW!",
2037                  HELIX_STRUCT);
2038
2039        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(70), 2, ""));           // insert behind right helix end
2040        TEST_ALI_LEN_ALIGNED(79, 1);
2041        TEST_DATA(".....G---G--G--C---C-G...--A--G--GAA-CCU--G---CG--G--C-UGG--AUC--ACCUCC........",
2042                  "-----A---C--G--A---U-C-----C--G--GAA-CCU--G---CG--G--C-UGG--AUC--ACCUCCU.......",
2043                  ".....A---C--G--A---A-C.....G--G--GAA-CCU--G---CG--G--C-UGG--AUC--ACCUCCU-------",
2044                  "-----U---G--C--C---U-G-----G--C--CCU-UAG--C---GC--G--G-UGG--UCC--CACCUGA.......",
2045                  ".........[..<..[...........[..[..[<<.[....]...>>..]......]..]......].>........]",
2046                  ".........1.....1...........25.25.34..34...3..4....3..4...25.25.....1..........1", // @@@ <- helix nr destroyed
2047                  ".........x........x........x...x.x....x...x..........x...x...x.....x...........x", // @@@ _REF gets destroyed here! (see #159)
2048                  "9003467004000009006035485565210094256820000006110060050200211390043589985100300",
2049                  "O!!Du8E!!J!!h!!6!!0e1XYLgxvzrqmeMiMAjB5E!!J!!xT6!!J!!PiCvQrq4uC!!LDoHlWV59!!DW!",
2050                  HELIX_STRUCT);
2051
2052
2053
2054        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(44), 2, ""));           // insert at gap border (between different gap types)
2055        TEST_ALI_LEN_ALIGNED(81, 1);
2056        TEST_DATA(".....G---G--G--C---C-G...----A--G--GAA-CCU--G---CG--G--C-UGG--AUC--ACCUCC........", // now prefers '-' here
2057                  "-----A---C--G--A---U-C-------C--G--GAA-CCU--G---CG--G--C-UGG--AUC--ACCUCCU.......",
2058                  ".....A---C--G--A---A-C.......G--G--GAA-CCU--G---CG--G--C-UGG--AUC--ACCUCCU-------",
2059                  "-----U---G--C--C---U-G-------G--C--CCU-UAG--C---GC--G--G-UGG--UCC--CACCUGA.......",
2060                  ".........[..<..[.............[..[..[<<.[....]...>>..]......]..]......].>........]",
2061                  ".........1.....1.............25.25.34..34...3..4....3..4...25.25.....1..........1",
2062                  ".........x........x..........x...x.x....x...x..........x...x...x.....x...........x",
2063                  "900346700400000900603548500565210094256820000006110060050200211390043589985100300",
2064                  "O!!Du8E!!J!!h!!6!!0e1XYLg!!xvzrqmeMiMAjB5E!!J!!xT6!!J!!PiCvQrq4uC!!LDoHlWV59!!DW!",
2065                  HELIX_STRUCT);
2066
2067
2068        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(42), -6, "-.")); // delete gaps
2069        TEST_ALI_LEN_ALIGNED(75, 1);
2070        TEST_DATA(".....G---G--G--C---C-G.A--G--GAA-CCU--G---CG--G--C-UGG--AUC--ACCUCC........",
2071                  "-----A---C--G--A---U-C-C--G--GAA-CCU--G---CG--G--C-UGG--AUC--ACCUCCU.......",
2072                  ".....A---C--G--A---A-C.G--G--GAA-CCU--G---CG--G--C-UGG--AUC--ACCUCCU-------",
2073                  "-----U---G--C--C---U-G-G--C--CCU-UAG--C---GC--G--G-UGG--UCC--CACCUGA.......",
2074                  ".........[..<..[.......[..[..[<<.[....]...>>..]......]..]......].>........]",
2075                  ".........1.....1.......25.25.34..34...3..4....3..4...25.25.....1..........1",
2076                  ".........x........x....x...x.x....x...x..........x...x...x.....x...........x",
2077                  "900346700400000900603545210094256820000006110060050200211390043589985100300",
2078                  "O!!Du8E!!J!!h!!6!!0e1XYzrqmeMiMAjB5E!!J!!xT6!!J!!PiCvQrq4uC!!LDoHlWV59!!DW!",
2079                  HELIX_STRUCT);
2080
2081        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(74), -1, "-.")); // delete gap inside helix destroying helix nrs
2082        TEST_ALI_LEN_ALIGNED(74, 1);
2083        TEST_DATA(".....G---G--G--C---C-G.A--G--GAA-CCU--G---CG--G--C-UGG-AUC--ACCUCC........",
2084                  "-----A---C--G--A---U-C-C--G--GAA-CCU--G---CG--G--C-UGG-AUC--ACCUCCU.......",
2085                  ".....A---C--G--A---A-C.G--G--GAA-CCU--G---CG--G--C-UGG-AUC--ACCUCCU-------",
2086                  "-----U---G--C--C---U-G-G--C--CCU-UAG--C---GC--G--G-UGG-UCC--CACCUGA.......",
2087                  ".........[..<..[.......[..[..[<<.[....]...>>..]......].]......].>........]",
2088                  ".........1.....1.......25.25.34..34...3..4....3..4...2525.....1..........1", // @@@ helix nr destroyed ('25.25' -> '2525')
2089                  ".........x........x....x...x.x....x...x..........x...x..x.....x...........x",
2090                  "90034670040000090060354521009425682000000611006005020021390043589985100300",
2091                  "O!!Du8E!!J!!h!!6!!0e1XYzrqmeMiMAjB5E!!J!!xT6!!J!!PiCvQr4uC!!LDoHlWV59!!DW!",
2092                  HELIX_STRUCT);
2093
2094
2095        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(47), -1, "-.")); // delete gap between helices destroying helix nrs
2096        TEST_ALI_LEN_ALIGNED(73, 1);
2097        TEST_DATA(".....G---G--G--C---C-G.A--G-GAA-CCU--G---CG--G--C-UGG-AUC--ACCUCC........",
2098                  "-----A---C--G--A---U-C-C--G-GAA-CCU--G---CG--G--C-UGG-AUC--ACCUCCU.......",
2099                  ".....A---C--G--A---A-C.G--G-GAA-CCU--G---CG--G--C-UGG-AUC--ACCUCCU-------",
2100                  "-----U---G--C--C---U-G-G--C-CCU-UAG--C---GC--G--G-UGG-UCC--CACCUGA.......",
2101                  ".........[..<..[.......[..[.[<<.[....]...>>..]......].]......].>........]",
2102                  ".........1.....1.......25.2534..34...3..4....3..4...2525.....1..........1", // @@@ helix nr destroyed ('25.34' -> '2534')
2103                  ".........x........x....x...xx....x...x..........x...x..x.....x...........x",
2104                  "9003467004000009006035452100425682000000611006005020021390043589985100300",
2105                  "O!!Du8E!!J!!h!!6!!0e1XYzrqmeiMAjB5E!!J!!xT6!!J!!PiCvQr4uC!!LDoHlWV59!!DW!",
2106                  HELIX_STRUCT);
2107
2108
2109        TEST_EXPECT_NO_ERROR(ARB_insdel_columns(gb_main, ali_name, COL(72), -5, "%")); // delete anything
2110        TEST_ALI_LEN_ALIGNED(68, 1);
2111        TEST_DATA(".....G---G--G--C---C-G.A--G-GAA-CCU--G---CG--G--C-UGG-ACCUCC........",
2112                  "-----A---C--G--A---U-C-C--G-GAA-CCU--G---CG--G--C-UGG-ACCUCCU.......",
2113                  ".....A---C--G--A---A-C.G--G-GAA-CCU--G---CG--G--C-UGG-ACCUCCU-------",
2114                  "-----U---G--C--C---U-G-G--C-CCU-UAG--C---GC--G--G-UGG-CACCUGA.......",
2115                  ".........[..<..[.......[..[.[<<.[....]...>>..]......]...].>........]",
2116                  ".........1.....1.......25.2534..34...3..4....3..4...2...1..........1",
2117                  ".........x........x....x...xx....x...x..........x...x...x...........x",
2118                  "90034670040000090060354521004256820000006110060050200043589985100300",
2119                  "O!!Du8E!!J!!h!!6!!0e1XYzrqmeiMAjB5E!!J!!xT6!!J!!PiCvQ!LDoHlWV59!!DW!",
2120                  HELIX_STRUCT);
2121
2122    }
2123
2124    if (!error) {
2125        {
2126            GB_transaction ta(gb_main);
2127            TEST_EXPECT_EQUAL(ARB_insdel_columns(gb_main, ali_name, COL(35), -3, "-."), // illegal delete
2128                              "SAI 'HELIX': You tried to delete 'x' at position 18  -> Operation aborted");
2129            ta.close("xxx");
2130        }
2131        {
2132            GB_transaction ta(gb_main);
2133            TEST_EXPECT_EQUAL(ARB_insdel_columns(gb_main, ali_name, COL(57), -3, "-."), // illegal delete
2134                              "SAI 'HELIX_NR': You tried to delete '4' at position 40  -> Operation aborted");
2135            ta.close("xxx");
2136        }
2137        {
2138            GB_transaction ta(gb_main);
2139            TEST_EXPECT_EQUAL(ARB_insdel_columns(gb_main, ali_name, 4711, 3, "-."), // illegal insert
2140                              "Can't insert at position 4711 (exceeds length 68 of alignment 'ali_mini')");
2141            ta.close("xxx");
2142        }
2143        {
2144            GB_transaction ta(gb_main);
2145            TEST_EXPECT_EQUAL(ARB_insdel_columns(gb_main, ali_name, 66, -3, "-."), // illegal delete
2146                              "Can't delete positions 66-68 (exceeds max. position 67 of alignment 'ali_mini')");
2147            ta.close("xxx");
2148        }
2149        {
2150            GB_transaction ta(gb_main);
2151            TEST_EXPECT_EQUAL(ARB_insdel_columns(gb_main, ali_name, -1, 3, "-."), // illegal insert
2152                              "Illegal sequence position -1");
2153            ta.close("xxx");
2154        }
2155    }
2156    if (!error) {
2157        GB_transaction ta(gb_main);
2158        TEST_DATA(".....G---G--G--C---C-G.A--G-GAA-CCU--G---CG--G--C-UGG-ACCUCC........",
2159                  "-----A---C--G--A---U-C-C--G-GAA-CCU--G---CG--G--C-UGG-ACCUCCU.......",
2160                  ".....A---C--G--A---A-C.G--G-GAA-CCU--G---CG--G--C-UGG-ACCUCCU-------",
2161                  "-----U---G--C--C---U-G-G--C-CCU-UAG--C---GC--G--G-UGG-CACCUGA.......",
2162                  ".........[..<..[.......[..[.[<<.[....]...>>..]......]...].>........]",
2163                  ".........1.....1.......25.2534..34...3..4....3..4...2...1..........1",
2164                  ".........x........x....x...xx....x...x..........x...x...x...........x",
2165                  "90034670040000090060354521004256820000006110060050200043589985100300",
2166                  "O!!Du8E!!J!!h!!6!!0e1XYzrqmeiMAjB5E!!J!!xT6!!J!!PiCvQ!LDoHlWV59!!DW!",
2167                  HELIX_STRUCT);
2168    }
2169
2170    GB_close(gb_main);
2171    TEST_EXPECT_NO_ERROR(error.deliver());
2172}
2173void TEST_insert_delete_DB() {
2174    test_insert_delete_DB(); // wrap test code in subroutine (otherwise nm 2.24 fails to provide source-location, even if TEST_PUBLISH is used)
2175}
2176
2177__ATTR__REDUCED_OPTIMIZE void TEST_insert_delete_DB_using_SAI() {
2178    GB_shell    shell;
2179    ARB_ERROR   error;
2180    const char *ali_name = "ali_mini";
2181    GBDATA     *gb_main  = TEST_CREATE_DB(error, ali_name, TADinsdel, false);
2182
2183    arb_suppress_progress noProgress;
2184
2185    if (!error) error = add_some_SAIs(gb_main, ali_name);
2186    if (!error) {
2187        GB_transaction ta(gb_main);
2188
2189        // test here is just a duplicate from TEST_insert_delete_DB() - just here to show the data
2190        TEST_EXPECT_NO_ERROR(ARB_format_alignment(gb_main, ali_name));
2191        int alilen_exp = 57;
2192        TEST_ALI_LEN_ALIGNED(alilen_exp, 1);
2193        TEST_DATA("...G-GGC-C-G...--A--G--GAA-CCUG-CGGC-UGG--AUCACCUCC......",
2194                  "---A-CGA-U-C-----C--G--GAA-CCUG-CGGC-UGG--AUCACCUCCU.....",
2195                  "...A-CGA-A-C.....G--G--GAA-CCUG-CGGC-UGG--AUCACCUCCU-----",
2196                  "---U-GCC-U-G-----G--C--CCU-UAGC-GCGG-UGG--UCCCACCUGA.....",
2197                  ".....[<[.........[..[..[<<.[..].>>]....]..]....].>......]",
2198                  ".....1.1.........25.25.34..34.34..34...25.25...1........1",
2199                  ".....x..x........x...x.x....x.x....x...x...x...x.........x",
2200                  "934674096035485565210094256820061165020021139435899851300",
2201                  "ODu8EJh60e1XYLgxvzrqmeMiMAjB5EJxT6JPiCvQrq4uCLDoHlWV59DW!",
2202                  HELIX_STRUCT);
2203
2204        RangeList delRanges  = build_RangeList_from_string(
2205            /* */ "xxx-------x-x-xxx---------x---------x---------------xxxx-",
2206            "x", false);
2207        TEST_EXPECT_NO_ERROR(ARB_delete_columns_using_SAI(gb_main, ali_name, delRanges, ".-"));
2208        alilen_exp -= 14;
2209        TEST_ALI_LEN_ALIGNED(alilen_exp, 1);
2210        TEST_DATA("G-GGC-CG.A--G--GAACCUG-CGGCUGG--AUCACCUCC..",
2211                  "A-CGA-UC-C--G--GAACCUG-CGGCUGG--AUCACCUCCU.",
2212                  "A-CGA-AC.G--G--GAACCUG-CGGCUGG--AUCACCUCCU-",
2213                  "U-GCC-UG-G--C--CCUUAGC-GCGGUGG--UCCCACCUGA.",
2214                  "..[<[....[..[..[<<[..].>>]...]..]....].>..]",
2215                  "..1.1....25.25.34.34.34..34..25.25...1....1",
2216                  "..x..x...x...x.x...x.x....x..x...x...x.....x",
2217                  "6740960585210094258200611652002113943589980",
2218                  "8EJh60eXLzrqmeMiMAB5EJxT6JPCvQrq4uCLDoHlWV!",
2219                  HELIX_STRUCT);
2220
2221        // insert INFRONTOF each range
2222        RangeList insRanges = build_RangeList_from_string(
2223            /* */ "---xx---xxxxxxxx---------xxxx--------------",
2224            "x", false);
2225        TEST_EXPECT_NO_ERROR(ARB_insert_columns_using_SAI(gb_main, ali_name, insRanges, RANGES, INFRONTOF, 2));
2226        alilen_exp += 3*2;
2227        TEST_ALI_LEN_ALIGNED(alilen_exp, 1);
2228        TEST_DATA("G-G--GC-CG...A--G--GAACCUG-CG--GCUGG--AUCACCUCC..",
2229                  "A-C--GA-UC---C--G--GAACCUG-CG--GCUGG--AUCACCUCCU.",
2230                  "A-C--GA-AC...G--G--GAACCUG-CG--GCUGG--AUCACCUCCU-",
2231                  "U-G--CC-UG---G--C--CCUUAGC-GC--GGUGG--UCCCACCUGA.",
2232                  "..[..<[......[..[..[<<[..].>>..]...]..]....].>..]",
2233                  "..1...1......25.25.34.34.34....34..25.25...1....1",
2234                  "..x....x.....x...x.x...x.x......x..x...x...x.....x",
2235                  "6740009605008521009425820061100652002113943589980",
2236                  "8EJ!!h60eX!!LzrqmeMiMAB5EJxT6!!JPCvQrq4uCLDoHlWV!",
2237                  HELIX_STRUCT);
2238
2239        // insert BEHIND each range
2240        insRanges = build_RangeList_from_string(
2241            /* */ "-----------xx-x------------xxxxxx---------------x",
2242            "x", false);
2243        TEST_EXPECT_NO_ERROR(ARB_insert_columns_using_SAI(gb_main, ali_name, insRanges, RANGES, BEHIND, 4));
2244        alilen_exp += 4*4;
2245        TEST_ALI_LEN_ALIGNED(alilen_exp, 1);
2246        TEST_DATA("G-G--GC-CG.......A------G--GAACCUG-CG--GC----UGG--AUCACCUCC......",
2247                  "A-C--GA-UC-------C------G--GAACCUG-CG--GC----UGG--AUCACCUCCU.....",
2248                  "A-C--GA-AC.......G------G--GAACCUG-CG--GC----UGG--AUCACCUCCU-----",
2249                  "U-G--CC-UG-------G------C--CCUUAGC-GC--GG----UGG--UCCCACCUGA.....",
2250                  "..[..<[..........[......[..[<<[..].>>..].......]..]....].>..]....",
2251                  "..1...1..........25.....25.34.34.34....34......25.25...1....1....",
2252                  "..x....x.........x.......x.x...x.x......x......x...x...x.........x", // @@@ ref gets destroyed here
2253                  "67400096050080000520000100942582006110065000020021139435899800000",
2254                  "8EJ!!h60eX!!L!!!!zr!!!!qmeMiMAB5EJxT6!!JP!!!!CvQrq4uCLDoHlWV!!!!!",
2255                  HELIX_STRUCT);
2256
2257        // insert INFRONTOF each column
2258        insRanges = build_RangeList_from_string(
2259            /* */ "x----xx--------------------------------------xxx----xxxx--------x",
2260            "x", false);
2261        TEST_EXPECT_NO_ERROR(ARB_insert_columns_using_SAI(gb_main, ali_name, insRanges, SINGLE_COLUMNS, INFRONTOF, 1));
2262        alilen_exp += 11*1;
2263        TEST_ALI_LEN_ALIGNED(alilen_exp, 1);
2264        TEST_DATA(".G-G---G-C-CG.......A------G--GAACCUG-CG--GC-----U-G-G--AU-C-A-C-CUCC.......",
2265                  ".A-C---G-A-UC-------C------G--GAACCUG-CG--GC-----U-G-G--AU-C-A-C-CUCCU......",
2266                  ".A-C---G-A-AC.......G------G--GAACCUG-CG--GC-----U-G-G--AU-C-A-C-CUCCU------",
2267                  ".U-G---C-C-UG-------G------C--CCUUAGC-GC--GG-----U-G-G--UC-C-C-A-CCUGA......",
2268                  "...[...<.[..........[......[..[<<[..].>>..]..........]..]........].>..].....",
2269                  "...1.....1..........25.....25.34.34.34....34.........25.25.......1....1.....",
2270                  "...x......x.........x.......x.x...x.x......x.........x...x.......x..........x",
2271                  "0674000009605008000052000010094258200611006500000200002113090403058998000000",
2272                  "!8EJ!!!h!60eX!!L!!!!zr!!!!qmeMiMAB5EJxT6!!JP!!!!!C!v!Qrq4u!C!L!D!oHlWV!!!!!!",
2273                  HELIX_STRUCT);
2274
2275        // insert BEHIND each column
2276        insRanges   = build_RangeList_from_string(
2277            /* */ "------------------------------xxxxxxx----------------------------xxxxxx-----",
2278            "x", false);
2279        TEST_EXPECT_NO_ERROR(ARB_insert_columns_using_SAI(gb_main, ali_name, insRanges, SINGLE_COLUMNS, BEHIND, 2));
2280        alilen_exp += 13*2;
2281        TEST_ALI_LEN_ALIGNED(alilen_exp, 1);
2282        TEST_DATA(".G-G---G-C-CG.......A------G--G--A--A--C--C--U--G---CG--GC-----U-G-G--AU-C-A-C-C--U--C--C.............",
2283                  ".A-C---G-A-UC-------C------G--G--A--A--C--C--U--G---CG--GC-----U-G-G--AU-C-A-C-C--U--C--C--U..........",
2284                  ".A-C---G-A-AC.......G------G--G--A--A--C--C--U--G---CG--GC-----U-G-G--AU-C-A-C-C--U--C--C--U----------",
2285                  ".U-G---C-C-UG-------G------C--C--C--U--U--A--G--C---GC--GG-----U-G-G--UC-C-C-A-C--C--U--G--A..........",
2286                  "...[...<.[..........[......[..[..<..<..[........]...>>..]..........]..]........].....>........].......",
2287                  "...1.....1..........25.....25.3..4.....3..4.....3..4....34.........25.25.......1..............1.......",  // @@@ helix nrs destroyed
2288                  "...x......x.........x.......x.x...........x.....x........x.........x...x.......x......................x", // @@@ ref destroyed further
2289                  "067400000960500800005200001009400200500800200000000611006500000200002113090403050080090090080000000000",
2290                  "!8EJ!!!h!60eX!!L!!!!zr!!!!qmeMi!!M!!A!!B!!5!!E!!J!!xT6!!JP!!!!!C!v!Qrq4u!C!L!D!o!!H!!l!!W!!V!!!!!!!!!!",
2291                  HELIX_STRUCT);
2292    }
2293
2294    GB_close(gb_main);
2295    TEST_EXPECT_NO_ERROR(error.deliver());
2296}
2297TEST_PUBLISH(TEST_insert_delete_DB_using_SAI);
2298
2299#endif // UNIT_TESTS
2300
Note: See TracBrowser for help on using the repository browser.