1// derived.cpp --
2// $Id: derived.cpp 1230 2007-03-09 15:58:53Z jcw $
3// This is part of Metakit, see http://www.equi4.com/metakit.html
4
5/** @file
6 * Derived views are virtual views which track changes
7 */
8
9#include "header.h"
10#include "handler.h"
11#include "store.h"
12#include "derived.h"
13
14#include <stdlib.h>   // qsort
15
16/////////////////////////////////////////////////////////////////////////////
17// Implemented in this file
18
19//  class c4_Sequence;
20class c4_DerivedSeq;
21class c4_FilterSeq;
22class c4_SortSeq;
23class c4_ProjectSeq;
24
25/////////////////////////////////////////////////////////////////////////////
26
27class c4_FilterSeq: public c4_DerivedSeq {
28  protected:
29    c4_DWordArray _rowMap;
30    c4_DWordArray _revMap;
31    c4_Row _lowRow;
32    c4_Row _highRow;
33    c4_Bytes _rowIds;
34
35  protected:
36    c4_FilterSeq(c4_Sequence &seq_);
37    virtual ~c4_FilterSeq();
38
39    void FixupReverseMap();
40    int PosInMap(int index_)const;
41    bool Match(int index_, c4_Sequence &seq_, const int * = 0, const int * = 0)
42      const;
43    bool MatchOne(int prop_, const c4_Bytes &data_)const;
44
45  public:
46    c4_FilterSeq(c4_Sequence &seq_, c4_Cursor low_, c4_Cursor high_);
47
48    virtual int RemapIndex(int, const c4_Sequence*)const;
49
50    virtual int NumRows()const;
51
52    virtual int Compare(int, c4_Cursor)const;
53    virtual bool Get(int, int, c4_Bytes &);
54
55    virtual void InsertAt(int, c4_Cursor, int = 1);
56    virtual void RemoveAt(int, int = 1);
57    virtual void Set(int, const c4_Property &, const c4_Bytes &);
58    virtual void SetSize(int);
59
60    virtual c4_Notifier *PreChange(c4_Notifier &nf_);
61    virtual void PostChange(c4_Notifier &nf_);
62};
63
64/////////////////////////////////////////////////////////////////////////////
65
66c4_FilterSeq::c4_FilterSeq(c4_Sequence &seq_): c4_DerivedSeq(seq_) {
67  _rowMap.SetSize(_seq.NumRows());
68  _revMap.SetSize(_seq.NumRows());
69  d4_assert(NumRows() == _seq.NumRows());
70
71  for (int i = 0; i < NumRows(); ++i) {
72    _rowMap.SetAt(i, i);
73    _revMap.SetAt(i, i);
74  }
75}
76
77c4_FilterSeq::c4_FilterSeq(c4_Sequence &seq_, c4_Cursor low_, c4_Cursor high_):
78  c4_DerivedSeq(seq_), _lowRow(*low_), _highRow(*high_) {
79  d4_assert((&_lowRow)._index == 0);
80  d4_assert((&_highRow)._index == 0);
81
82  // use a sneaky way to obtain the sequence pointers and indices
83  c4_Sequence *lowSeq = (&_lowRow)._seq;
84  c4_Sequence *highSeq = (&_highRow)._seq;
85  d4_assert(lowSeq && highSeq);
86
87  // prepare column numbers to avoid looking them up on every row
88  // lowCols is a vector of column numbers to use for the low limits
89  // highCols is a vector of column numbers to use for the high limits
90  int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers();
91  c4_Bytes lowVec, highVec;
92  int *lowCols = (int*)lowVec.SetBufferClear(nl *sizeof(int));
93  int *highCols = (int*)highVec.SetBufferClear(nh *sizeof(int));
94
95  for (int il = 0; il < nl; ++il)
96    lowCols[il] = seq_.PropIndex(lowSeq->NthPropId(il));
97  for (int ih = 0; ih < nh; ++ih)
98    highCols[ih] = seq_.PropIndex(highSeq->NthPropId(ih));
99
100  // set _rowIds flag buffer for fast matching
101   {
102    int max =  - 1;
103
104     {
105      for (int i1 = 0; i1 < nl; ++i1) {
106        int n = lowSeq->NthPropId(i1);
107        if (max < n)
108          max = n;
109      }
110      for (int i2 = 0; i2 < nh; ++i2) {
111        int n = highSeq->NthPropId(i2);
112        if (max < n)
113          max = n;
114      }
115    }
116
117    t4_byte *p = _rowIds.SetBufferClear(max + 1);
118
119     {
120      for (int i1 = 0; i1 < nl; ++i1)
121        p[lowSeq->NthPropId(i1)] |= 1;
122      for (int i2 = 0; i2 < nh; ++i2)
123        p[highSeq->NthPropId(i2)] |= 2;
124    }
125  }
126
127  // now go through all rows and select the ones that are in range
128
129  _rowMap.SetSize(_seq.NumRows()); // avoid growing, use safe upper bound
130
131  int n = 0;
132
133  for (int i = 0; i < _seq.NumRows(); ++i)
134    if (Match(i, _seq, lowCols, highCols))
135      _rowMap.SetAt(n++, i);
136
137  _rowMap.SetSize(n);
138
139  FixupReverseMap();
140}
141
142c4_FilterSeq::~c4_FilterSeq(){}
143
144void c4_FilterSeq::FixupReverseMap() {
145  int n = _seq.NumRows();
146
147  _revMap.SetSize(0);
148
149  if (n > 0) {
150    _revMap.InsertAt(0, ~(t4_i32)0, n); //!
151
152    for (int i = 0; i < _rowMap.GetSize(); ++i)
153      _revMap.SetAt((int)_rowMap.GetAt(i), i);
154  }
155}
156
157bool c4_FilterSeq::MatchOne(int prop_, const c4_Bytes &data_)const {
158  d4_assert(prop_ < _rowIds.Size());
159
160  t4_byte flag = _rowIds.Contents()[prop_];
161  d4_assert(flag);
162
163  if (flag &1) {
164    c4_Sequence *lowSeq = (&_lowRow)._seq;
165
166    c4_Handler &h = lowSeq->NthHandler(lowSeq->PropIndex(prop_));
167    if (h.Compare(0, data_) > 0)
168      return false;
169  }
170
171  if (flag &2) {
172    c4_Sequence *highSeq = (&_highRow)._seq;
173
174    c4_Handler &h = highSeq->NthHandler(highSeq->PropIndex(prop_));
175    if (h.Compare(0, data_) < 0)
176      return false;
177  }
178
179  return true;
180}
181
182bool c4_FilterSeq::Match(int index_, c4_Sequence &seq_, const int *lowCols_,
183  const int *highCols_)const {
184  // use a sneaky way to obtain the sequence pointers and indices
185  c4_Sequence *lowSeq = (&_lowRow)._seq;
186  c4_Sequence *highSeq = (&_highRow)._seq;
187  d4_assert(lowSeq && highSeq);
188
189  int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers();
190
191  c4_Bytes data;
192
193  // check each of the lower limits
194  for (int cl = 0; cl < nl; ++cl) {
195    c4_Handler &hl = lowSeq->NthHandler(cl);
196
197    int n = lowCols_ ? lowCols_[cl]: seq_.PropIndex(lowSeq->NthPropId(cl));
198    if (n >= 0) {
199      c4_Handler &h = seq_.NthHandler(n);
200      const c4_Sequence *hc = seq_.HandlerContext(n);
201      int i = seq_.RemapIndex(index_, hc);
202
203      h.GetBytes(i, data);
204    } else
205      hl.ClearBytes(data);
206
207    if (hl.Compare(0, data) > 0)
208      return false;
209  }
210
211  // check each of the upper limits
212  for (int ch = 0; ch < nh; ++ch) {
213    c4_Handler &hh = highSeq->NthHandler(ch);
214
215    int n = highCols_ ? highCols_[ch]: seq_.PropIndex(highSeq->NthPropId(ch));
216    if (n >= 0) {
217      c4_Handler &h = seq_.NthHandler(n);
218      const c4_Sequence *hc = seq_.HandlerContext(n);
219      int i = seq_.RemapIndex(index_, hc);
220
221      h.GetBytes(i, data);
222    } else
223      hh.ClearBytes(data);
224
225    if (hh.Compare(0, data) < 0)
226      return false;
227  }
228
229  return true;
230}
231
232int c4_FilterSeq::RemapIndex(int index_, const c4_Sequence *seq_)const {
233  return seq_ == this ? index_: _seq.RemapIndex((int)_rowMap.GetAt(index_),
234    seq_);
235}
236
237int c4_FilterSeq::NumRows()const {
238  return _rowMap.GetSize();
239}
240
241int c4_FilterSeq::Compare(int index_, c4_Cursor cursor_)const {
242  return _seq.Compare((int)_rowMap.GetAt(index_), cursor_);
243}
244
245bool c4_FilterSeq::Get(int index_, int propId_, c4_Bytes &bytes_) {
246  return _seq.Get((int)_rowMap.GetAt(index_), propId_, bytes_);
247}
248
249void c4_FilterSeq::InsertAt(int, c4_Cursor, int) {
250  d4_assert(0);
251}
252
253void c4_FilterSeq::RemoveAt(int, int) {
254  d4_assert(0);
255}
256
257void c4_FilterSeq::Set(int, const c4_Property &, const c4_Bytes &) {
258  d4_assert(0);
259}
260
261void c4_FilterSeq::SetSize(int) {
262  d4_assert(0);
263}
264
265int c4_FilterSeq::PosInMap(int index_)const {
266  int i = 0;
267
268  while (i < NumRows())
269    if ((int)_rowMap.GetAt(i) >= index_)
270      break;
271    else
272      ++i;
273
274  return i;
275}
276
277c4_Notifier *c4_FilterSeq::PreChange(c4_Notifier &nf_) {
278  if (!GetDependencies())
279    return 0;
280
281  c4_Notifier *chg = d4_new c4_Notifier(this);
282
283  bool pass = false;
284
285  switch (nf_._type) {
286    case c4_Notifier::kSet: pass = nf_._propId >= _rowIds.Size() ||
287      _rowIds.Contents()[nf_._propId] == 0;
288    // fall through...
289
290    case c4_Notifier::kSetAt:  {
291      int r = (int)_revMap.GetAt(nf_._index);
292
293      bool includeRow = r >= 0;
294      if (!pass)
295      if (nf_._type == c4_Notifier::kSetAt) {
296        d4_assert(nf_._cursor != 0);
297        includeRow = Match(nf_._cursor->_index, *nf_._cursor->_seq);
298      }
299       else
300      // set just one property, and it's not in a row yet
301        includeRow = MatchOne(nf_._propId,  *nf_._bytes);
302
303      if (r >= 0 && !includeRow)
304        chg->StartRemoveAt(r, 1);
305      else if (r < 0 && includeRow)
306        chg->StartInsertAt(PosInMap(nf_._index),  *nf_._cursor, 1);
307      else if (includeRow) {
308        d4_assert(r >= 0);
309
310        if (nf_._type == c4_Notifier::kSetAt)
311          chg->StartSetAt(r,  *nf_._cursor);
312        else
313          chg->StartSet(r, nf_._propId,  *nf_._bytes);
314      }
315    }
316    break;
317
318    case c4_Notifier::kInsertAt:  {
319      int i = PosInMap(nf_._index);
320
321      d4_assert(nf_._cursor != 0);
322      if (Match(nf_._cursor->_index,  *nf_._cursor->_seq))
323        chg->StartInsertAt(i,  *nf_._cursor, nf_._count);
324    }
325    break;
326
327    case c4_Notifier::kRemoveAt:  {
328      int i = PosInMap(nf_._index);
329      int j = PosInMap(nf_._index + nf_._count);
330      d4_assert(j >= i);
331
332      if (j > i)
333        chg->StartRemoveAt(i, j - i);
334    }
335    break;
336
337    case c4_Notifier::kMove:  {
338      int i = PosInMap(nf_._index);
339      bool inMap = i < NumRows() && (int)_rowMap.GetAt(i) == nf_._index;
340
341      if (inMap && nf_._index != nf_._count)
342        chg->StartMove(i, PosInMap(nf_._count));
343    }
344    break;
345  }
346
347  return chg;
348}
349
350void c4_FilterSeq::PostChange(c4_Notifier &nf_) {
351  bool pass = false;
352
353  switch (nf_._type) {
354    case c4_Notifier::kSet: pass = nf_._propId >= _rowIds.Size() ||
355      _rowIds.Contents()[nf_._propId] == 0;
356    // fall through...
357
358    case c4_Notifier::kSetAt:  {
359      int r = (int)_revMap.GetAt(nf_._index);
360
361      bool includeRow = r >= 0;
362      if (!pass)
363      if (nf_._type == c4_Notifier::kSetAt) {
364        d4_assert(nf_._cursor != 0);
365        includeRow = Match(nf_._cursor->_index, *nf_._cursor->_seq);
366      }
367       else
368      // set just one property, and it's not in a row yet
369        includeRow = MatchOne(nf_._propId,  *nf_._bytes);
370
371      if (r >= 0 && !includeRow)
372        _rowMap.RemoveAt(r);
373      else if (r < 0 && includeRow)
374        _rowMap.InsertAt(PosInMap(nf_._index), nf_._index);
375      else
376        break;
377
378      FixupReverseMap();
379    }
380    break;
381
382    case c4_Notifier::kInsertAt:  {
383      int i = PosInMap(nf_._index);
384
385      if (Match(nf_._index, _seq)) {
386        _rowMap.InsertAt(i, 0, nf_._count);
387
388        for (int j = 0; j < nf_._count; ++j)
389          _rowMap.SetAt(i++, nf_._index + j);
390      }
391
392      while (i < NumRows())
393        _rowMap.ElementAt(i++) += nf_._count;
394
395      FixupReverseMap();
396    }
397    break;
398
399    case c4_Notifier::kRemoveAt:  {
400      int i = PosInMap(nf_._index);
401      int j = PosInMap(nf_._index + nf_._count);
402      d4_assert(j >= i);
403
404      if (j > i)
405        _rowMap.RemoveAt(i, j - i);
406
407      while (i < NumRows())
408        _rowMap.ElementAt(i++) -= nf_._count;
409
410      FixupReverseMap();
411    }
412    break;
413
414    case c4_Notifier::kMove:  {
415      int i = PosInMap(nf_._index);
416      bool inMap = i < NumRows() && (int)_rowMap.GetAt(i) == nf_._index;
417
418      if (inMap && nf_._index != nf_._count) {
419        int j = PosInMap(nf_._count);
420
421        _rowMap.RemoveAt(i);
422
423        if (j > i)
424          --j;
425
426        _rowMap.InsertAt(j, nf_._count);
427
428        FixupReverseMap();
429      }
430    }
431    break;
432  }
433}
434
435/////////////////////////////////////////////////////////////////////////////
436
437class c4_SortSeq: public c4_FilterSeq {
438  public:
439    typedef t4_i32 T;
440
441    c4_SortSeq(c4_Sequence &seq_, c4_Sequence *down_);
442    virtual ~c4_SortSeq();
443
444    virtual c4_Notifier *PreChange(c4_Notifier &nf_);
445    virtual void PostChange(c4_Notifier &nf_);
446
447  private:
448    struct c4_SortInfo {
449        c4_Handler *_handler;
450        const c4_Sequence *_context;
451        c4_Bytes _buffer;
452
453        int CompareOne(c4_Sequence &seq_, T a, T b) {
454            _handler->GetBytes(seq_.RemapIndex((int)b, _context), _buffer, true)
455              ;
456            return _handler->Compare(seq_.RemapIndex((int)a, _context), _buffer)
457              ;
458        }
459    };
460
461    bool LessThan(T a, T b);
462    bool TestSwap(T &first, T &second);
463    void MergeSortThis(T *ar, int size, T scratch[]);
464    void MergeSort(T ar[], int size);
465
466    virtual int Compare(int, c4_Cursor)const;
467    int PosInMap(c4_Cursor cursor_)const;
468
469    c4_SortInfo *_info;
470    c4_Bytes _down;
471    int _width;
472};
473
474/////////////////////////////////////////////////////////////////////////////
475
476bool c4_SortSeq::LessThan(T a, T b) {
477  if (a == b)
478    return false;
479
480  // go through each of the columns and compare values, but since
481  // handler access is used, we must be careful to remap indices
482
483  c4_SortInfo *info;
484
485  for (info = _info; info->_handler; ++info) {
486    int f = info->CompareOne(_seq, a, b);
487    if (f) {
488      int n = info - _info;
489      if (_width < n)
490        _width = n;
491
492      return (_down.Contents()[n] ?  - f: f) < 0;
493    }
494  }
495
496  _width = info - _info;
497  return a < b;
498}
499
500inline bool c4_SortSeq::TestSwap(T &first, T &second) {
501  if (LessThan(second, first)) {
502    T temp = first;
503    first = second;
504    second = temp;
505    return true;
506  }
507
508  return false;
509}
510
511void c4_SortSeq::MergeSortThis(T *ar, int size, T scratch[]) {
512  switch (size) {
513    //Handle the special cases for speed:
514    case 2:
515      TestSwap(ar[0], ar[1]);
516      break;
517
518    case 3:
519      TestSwap(ar[0], ar[1]);
520      if (TestSwap(ar[1], ar[2]))
521        TestSwap(ar[0], ar[1]);
522      break;
523
524    case 4:
525      //Gotta optimize this....
526      TestSwap(ar[0], ar[1]);
527      TestSwap(ar[2], ar[3]);
528      TestSwap(ar[0], ar[2]);
529      TestSwap(ar[1], ar[3]);
530      TestSwap(ar[1], ar[2]);
531      break;
532
533      //Gotta do special case for list of five.
534
535    default:
536      //Subdivide the list, recurse, and merge
537       {
538        int s1 = size / 2;
539        int s2 = size - s1;
540        T *from1_ = scratch;
541        T *from2_ = scratch + s1;
542        MergeSortThis(from1_, s1, ar);
543        MergeSortThis(from2_, s2, ar + s1);
544
545        T *to1_ = from1_ + s1;
546        T *to2_ = from2_ + s2;
547
548        for (;;) {
549          if (LessThan(*from1_,  *from2_)) {
550            *ar++ =  *from1_++;
551
552            if (from1_ >= to1_) {
553              while (from2_ < to2_)
554                *ar++ =  *from2_++;
555              break;
556            }
557          }
558           else {
559            *ar++ =  *from2_++;
560
561            if (from2_ >= to2_) {
562              while (from1_ < to1_)
563                *ar++ =  *from1_++;
564              break;
565            }
566          }
567        }
568      }
569  }
570}
571
572void c4_SortSeq::MergeSort(T ar[], int size) {
573  if (size > 1) {
574    T *scratch = d4_new T[size];
575    memcpy(scratch, ar, size *sizeof(T));
576    MergeSortThis(ar, size, scratch);
577    delete [] scratch;
578  }
579}
580
581c4_SortSeq::c4_SortSeq(c4_Sequence &seq_, c4_Sequence *down_): c4_FilterSeq
582  (seq_), _info(0), _width( - 1) {
583  d4_assert(NumRows() == seq_.NumRows());
584
585  if (NumRows() > 0) {
586    // down is a vector of flags, true to sort in reverse order
587    char *down = (char*)_down.SetBufferClear(NumHandlers());
588
589    // set the down flag for all properties to be sorted in reverse
590    if (down_)
591      for (int i = 0; i < NumHandlers(); ++i)
592        if (down_->PropIndex(NthPropId(i)) >= 0)
593          down[i] = 1;
594
595    _width =  - 1;
596    int n = NumHandlers() + 1;
597    _info = d4_new c4_SortInfo[n];
598
599    int j;
600
601    for (j = 0; j < NumHandlers(); ++j) {
602      _info[j]._handler = &_seq.NthHandler(j);
603      _info[j]._context = _seq.HandlerContext(j);
604    }
605
606    _info[j]._handler = 0;
607
608    // everything is ready, go sort the row index vector
609    MergeSort((T*) &_rowMap.ElementAt(0), NumRows());
610
611    delete [] _info;
612    _info = 0;
613
614    FixupReverseMap();
615  }
616}
617
618c4_SortSeq::~c4_SortSeq() {
619  d4_assert(!_info);
620}
621
622int c4_SortSeq::Compare(int index_, c4_Cursor cursor_)const {
623  d4_assert(cursor_._seq != 0);
624
625  const char *down = (const char*)_down.Contents();
626  d4_assert(_down.Size() <= NumHandlers());
627
628  c4_Bytes data;
629
630  for (int colNum = 0; colNum < NumHandlers(); ++colNum) {
631    c4_Handler &h = NthHandler(colNum);
632    const c4_Sequence *hc = HandlerContext(colNum);
633
634    if (!cursor_._seq->Get(cursor_._index, h.PropId(), data))
635      h.ClearBytes(data);
636
637    int f = h.Compare(RemapIndex(index_, hc), data);
638    if (f != 0)
639      return colNum < _down.Size() && down[colNum] ?  - f:  + f;
640  }
641
642  return 0;
643}
644
645int c4_SortSeq::PosInMap(c4_Cursor cursor_)const {
646  int i = 0;
647
648  while (i < NumRows())
649    if (Compare(i, cursor_) >= 0)
650      break;
651    else
652      ++i;
653
654  d4_assert(i == NumRows() || Compare(i, cursor_) >= 0);
655  return i;
656}
657
658c4_Notifier *c4_SortSeq::PreChange(c4_Notifier & /*nf_*/) {
659  if (!GetDependencies())
660    return 0;
661
662#if 0
663  c4_Notifier *chg = d4_new c4_Notifier(this);
664
665  switch (nf_._type) {
666    case c4_Notifier::kSetAt: case c4_Notifier::kSet:  {
667      d4_assert(0); // also needs nested propagation
668
669      /*
670      change can require a move *and* a change of contents
671       */
672    }
673    break;
674
675    case c4_Notifier::kInsertAt:  {
676      d4_assert(0); // this case isn't really difficult
677    }
678    break;
679
680    case c4_Notifier::kRemoveAt:  {
681      d4_assert(0); // nested propagation is too difficult for now
682      // i.e. can only use sort as last derived view
683      /*
684      possible solution:
685
686      if 1 row, simple
687      else if contig in map, also simple
688      else propagate reorder first, then delete contig
689
690      it can be done here, as multiple notifications,
691      by simulating n-1 SetAt's of first row in others
692      needs some map juggling, allow temp dup entries?
693
694      or perhaps more consistent with n separate removes
695       */
696    }
697    break;
698
699    case c4_Notifier::kMove:  {
700      // incorrect: may need to move if recnum matters (recs same)
701    }
702    break;
703  }
704
705  return chg;
706#endif
707
708  //  d4_assert(0); // fail, cannot handle a view dependent on this one yet
709  return 0;
710}
711
712void c4_SortSeq::PostChange(c4_Notifier &nf_) {
713  switch (nf_._type) {
714    case c4_Notifier::kSet: if (_seq.PropIndex(nf_._propId) > _width)
715      break;
716    // cannot affect sort order, valuable optimization
717
718    case c4_Notifier::kSetAt:  {
719      int oi = (int)_revMap.GetAt(nf_._index);
720      d4_assert(oi >= 0);
721
722      c4_Cursor cursor(_seq, nf_._index);
723
724      // move the entry if the sort order has been disrupted
725      if ((oi > 0 && Compare(oi - 1, cursor) > 0) || (oi + 1 < NumRows() &&
726        Compare(oi + 1, cursor) < 0)) {
727        _rowMap.RemoveAt(oi);
728        _rowMap.InsertAt(PosInMap(cursor), nf_._index);
729
730        FixupReverseMap();
731      }
732
733      _width = NumHandlers(); // sorry, no more optimization
734    }
735    break;
736
737    case c4_Notifier::kInsertAt:  {
738      // if cursor was not set, it started out as a single Set
739      c4_Cursor cursor(_seq, nf_._index);
740      if (nf_._cursor)
741        cursor =  *nf_._cursor;
742
743      for (int n = 0; n < NumRows(); ++n)
744        if ((int)_rowMap.GetAt(n) >= nf_._index)
745          _rowMap.ElementAt(n) += nf_._count;
746
747      int i = PosInMap(cursor);
748      _rowMap.InsertAt(i, 0, nf_._count);
749
750      for (int j = 0; j < nf_._count; ++j)
751        _rowMap.SetAt(i++, nf_._index + j);
752
753      FixupReverseMap();
754
755      _width = NumHandlers(); // sorry, no more optimization
756    }
757    break;
758
759    case c4_Notifier::kRemoveAt:  {
760      int lo = nf_._index;
761      int hi = nf_._index + nf_._count;
762
763      int j = 0;
764      for (int i = 0; i < NumRows(); ++i) {
765        int n = (int)_rowMap.GetAt(i);
766
767        if (n >= hi)
768          _rowMap.ElementAt(i) -= nf_._count;
769
770        if (!(lo <= n && n < hi))
771          _rowMap.SetAt(j++, _rowMap.GetAt(i));
772      }
773
774      d4_assert(j + nf_._count == NumRows());
775      _rowMap.SetSize(j);
776
777      FixupReverseMap();
778
779      _width = NumHandlers(); // sorry, no more optimization
780    }
781    break;
782
783    case c4_Notifier::kMove:  {
784      // incorrect: may need to move if recnum matters (recs same)
785    }
786    break;
787  }
788}
789
790/////////////////////////////////////////////////////////////////////////////
791
792class c4_ProjectSeq: public c4_DerivedSeq {
793    c4_DWordArray _colMap; // a bit large, but bytes would be too small
794    bool _frozen;
795    int _omitCount; // if > 0 then this is a dynamic "project without"
796
797  public:
798    c4_ProjectSeq(c4_Sequence &seq_, c4_Sequence &in_, bool, c4_Sequence *out_);
799    virtual ~c4_ProjectSeq();
800
801    virtual int NumHandlers()const;
802    virtual c4_Handler &NthHandler(int)const;
803    virtual const c4_Sequence *HandlerContext(int)const;
804    virtual int AddHandler(c4_Handler*);
805
806    virtual bool Get(int, int, c4_Bytes &);
807    virtual void Set(int, const c4_Property &, const c4_Bytes &);
808};
809
810/////////////////////////////////////////////////////////////////////////////
811
812c4_ProjectSeq::c4_ProjectSeq(c4_Sequence &seq_, c4_Sequence &in_, bool reorder_,
813  c4_Sequence *out_): c4_DerivedSeq(seq_), _frozen(!reorder_ && !out_),
814  _omitCount(0) {
815  // build the array with column indexes
816  for (int j = 0; j < in_.NumHandlers(); ++j) {
817    int propId = in_.NthPropId(j);
818    int idx = _seq.PropIndex(propId);
819
820    // if the j'th property is in the sequence, add it
821    if (idx >= 0) {
822      // but only if it's not in the out_ view
823      if (out_ && out_->PropIndex(propId) >= 0)
824        ++_omitCount;
825      else
826        _colMap.Add(idx);
827    }
828  }
829
830  // if only reordering, append remaining columns from original view
831  if (reorder_) {
832    for (int i = 0; i < _seq.NumHandlers(); ++i) {
833      int propId = _seq.NthPropId(i);
834
835      // only consider properties we did not deal with before
836      if (in_.PropIndex(propId) < 0)
837        _colMap.Add(i);
838    }
839
840    d4_assert(_colMap.GetSize() == _seq.NumHandlers());
841  }
842}
843
844c4_ProjectSeq::~c4_ProjectSeq(){}
845
846int c4_ProjectSeq::NumHandlers()const {
847  return _frozen ? _colMap.GetSize(): _seq.NumHandlers() - _omitCount;
848}
849
850c4_Handler &c4_ProjectSeq::NthHandler(int colNum_)const {
851  int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_): colNum_;
852  return _seq.NthHandler(n);
853}
854
855const c4_Sequence *c4_ProjectSeq::HandlerContext(int colNum_)const {
856  int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_): colNum_;
857  return _seq.HandlerContext(n);
858}
859
860int c4_ProjectSeq::AddHandler(c4_Handler *handler_) {
861  int n = _seq.AddHandler(handler_);
862  return _frozen ? _colMap.Add(n): n - _omitCount;
863}
864
865bool c4_ProjectSeq::Get(int index_, int propId_, c4_Bytes &buf_) {
866  // fixed in 1.8: check that the property is visible
867  return PropIndex(propId_) >= 0 && _seq.Get(index_, propId_, buf_);
868}
869
870void c4_ProjectSeq::Set(int index_, const c4_Property &prop_, const c4_Bytes
871  &bytes_) {
872  int n = _seq.NumHandlers();
873  _seq.Set(index_, prop_, bytes_);
874
875  // if the number of handlers changed, then one must have been added
876  if (n != _seq.NumHandlers()) {
877    d4_assert(n == _seq.NumHandlers() - 1);
878
879    if (_frozen)
880      _colMap.Add(n);
881  }
882}
883
884/////////////////////////////////////////////////////////////////////////////
885
886c4_Sequence *f4_CreateFilter(c4_Sequence &seq_, c4_Cursor l_, c4_Cursor h_) {
887  return d4_new c4_FilterSeq(seq_, l_, h_);
888}
889
890c4_Sequence *f4_CreateSort(c4_Sequence &seq_, c4_Sequence *down_) {
891  return d4_new c4_SortSeq(seq_, down_);
892}
893
894c4_Sequence *f4_CreateProject(c4_Sequence &seq_, c4_Sequence &in_, bool
895  reorder_, c4_Sequence *out_) {
896  return d4_new c4_ProjectSeq(seq_, in_, reorder_, out_);
897}
898
899/////////////////////////////////////////////////////////////////////////////
900