1 /*
2     This file is part of GNU APL, a free implementation of the
3     ISO/IEC Standard 13751, "Programming Language APL, Extended"
4 
5     Copyright (C) 2008-2019  Dr. Jürgen Sauermann
6 
7     This program is free software: you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation, either version 3 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #ifndef __BIF_F12_TAKE_DROP_HH_DEFINED__
22 #define __BIF_F12_TAKE_DROP_HH_DEFINED__
23 
24 #include "Common.hh"
25 #include "PrimitiveFunction.hh"
26 
27 //=============================================================================
28 /** primitive functions Take and First */
29 /// The class implementing ↑
30 class Bif_F12_TAKE : public NonscalarFunction
31 {
32 public:
33    /// Constructor
Bif_F12_TAKE()34    Bif_F12_TAKE()
35    : NonscalarFunction(TOK_F12_TAKE)
36    {}
37 
38    /// overloaded Function::eval_B()
eval_B(Value_P B)39    virtual Token eval_B(Value_P B)
40       { return Token(TOK_APL_VALUE1, first(B));}
41 
42    /// overloaded Function::eval_AB()
43    virtual Token eval_AB(Value_P A, Value_P B);
44 
45    /// overloaded Function::eval_AXB()
46    virtual Token eval_AXB(Value_P A, Value_P X, Value_P B);
47 
48    /// Take from B according to ravel_A
49    static Value_P do_take(const Shape & shape_Zi, Value_P B);
50 
51    /// Fill Z with B, pad as necessary
52    static void fill(const Shape & shape_Zi, Cell * cZ, Value & Z_owner,
53                     Value_P B);
54 
55    static Bif_F12_TAKE * fun;   ///< Built-in function
56    static Bif_F12_TAKE  _fun;   ///< Built-in function
57 
58    /// ↑B
59    static Value_P first(Value_P B);
60 
61 protected:
62    /// Take A from B
63    Token take(Value_P A, Value_P B);
64 };
65 //=============================================================================
66 /** primitive function drop */
67 /// The class implementing ↓
68 class Bif_F12_DROP : public NonscalarFunction
69 {
70 public:
71    /// Constructor
Bif_F12_DROP()72    Bif_F12_DROP()
73    : NonscalarFunction(TOK_F12_DROP)
74    {}
75 
76    /// overloaded Function::eval_AB()
77    virtual Token eval_AB(Value_P A, Value_P B);
78 
79    /// overloaded Function::eval_AXB()
80    virtual Token eval_AXB(Value_P A, Value_P X, Value_P B);
81 
82    static Bif_F12_DROP * fun;   ///< Built-in function
83    static Bif_F12_DROP  _fun;   ///< Built-in function
84 
85 protected:
86 
87 };
88 //=============================================================================
89 /** A helper class for Bif_F12_TAKE and Bif_F12_DROP. It implements an iterator
90     that iterates over the indices (as dictated by left argument A) of the
91     right argument B of A↑B or A↓B,
92  **/
93 class TakeDropIterator
94 {
95 public:
96    /// constructor
TakeDropIterator(bool take,const Shape & sh_A,const Shape & sh_B)97    TakeDropIterator(bool take, const Shape & sh_A, const Shape & sh_B)
98    : ref_B(sh_B),
99      current_offset(0),
100      outside_B(0),
101      has_overtake(false),
102      done(false)
103       {
104         ShapeItem _weight = 1;
105         loop(r, sh_A.get_rank())
106             {
107               const ShapeItem sA = sh_A.get_transposed_shape_item(r);
108               const ShapeItem sB = sh_B.get_transposed_shape_item(r);
109               ShapeItem _from, _to;
110               if (take)   // sh_A ↑ B
111                  {
112                    if (sA < 0)   // take from end
113                       {
114                         _to   = sB;
115                         _from = sB + sA;   // + since sA < 0
116                       }
117                    else          // take from start
118                       {
119                         _from = 0;
120                         _to   = sA;
121                       }
122 
123                    if (_from < 0 || _from >= sB)   outside_B |= 1ULL << r;
124                    if (_from < 0 || _to >= sB)     has_overtake = true;
125                  }
126               else        // sh_A ↓ B
127                  {
128                    if (sA < 0)   // drop from end
129                       {
130                         _from = 0;
131                         _to   = sB + sA;   // + since sA < 0
132                       }
133                    else          // drop from start
134                       {
135                         _from = sA;
136                         _to   = sB;
137                       }
138 
139                    if (_from >= _to)   // over-drop
140                       {
141                         _from = 0;
142                         _to   = 0;
143                       }
144                  }
145 
146               Assert(_from <= _to);
147 
148               _ftwc & ftwc_r = ftwc[r];
149               ftwc_r.from    = _from;
150               ftwc_r.to      = _to;
151               ftwc_r.weight  = _weight;
152               ftwc_r.current = _from;
153               current_offset += _from * _weight;
154 
155               if (_from == _to)   done = true;   // empty array
156 
157               _weight *= sB;
158             }
159       }
160 
161    /// the work-horse of the iterator
operator ++()162    void operator++()
163       {
164         loop(r, ref_B.get_rank())
165            {
166              _ftwc & ftwc_r = ftwc[r];
167              ++ftwc_r.current;
168               current_offset += ftwc_r.weight;
169               if (has_overtake)
170                  {
171                    if (ftwc_r.current < 0 ||
172                        ftwc_r.current >= ref_B.get_transposed_shape_item(r))
173                       outside_B |= 1ULL << r;
174                    else
175                       outside_B &= ~(1ULL << r);
176                  }
177 
178               if (ftwc_r.current < ftwc_r.to)   return;
179 
180               // end of dimension reached: reset this dimension
181               // and increment next dimension
182               //
183               current_offset -= ftwc_r.weight * (ftwc_r.current - ftwc_r.from);
184               ftwc_r.current = ftwc_r.from;
185               if (has_overtake)
186                  {
187                    if (ftwc_r.current < 0 ||
188                        ftwc_r.current >= ref_B.get_transposed_shape_item(r))
189                       outside_B |= 1ULL << r;
190                    else
191                       outside_B &= ~(1ULL << r);
192                  }
193            }
194         done = true;
195       }
196 
197    /// return true iff this inerator has more items to come.
more() const198    bool more() const   { return !done; }
199 
200    /// return the current offset (if inside B) or else -1.
operator ()() const201    ShapeItem operator()() const
202       { return outside_B ? -1 : current_offset; }
203 
204 protected:
205    /// shape of the source array
206    const Shape & ref_B;
207 
208    /// from / to / weight / current
209    struct _ftwc ftwc[MAX_RANK];
210 
211    /// the current offset from ↑B
212    ShapeItem current_offset;
213 
214    /// a bitmask of dimensions with overtake
215    ShapeItem outside_B;
216 
217    /// true iff over-Take of at least one dimension
218    bool has_overtake;
219 
220    /// true iff this interator has reached its final item
221    bool done;
222 };
223 //=============================================================================
224 #endif // __BIF_F12_TAKE_DROP_HH_DEFINED__
225