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 #include "Bif_OPER1_EACH.hh"
22 #include "Bif_F12_TAKE_DROP.hh"
23 #include "Workspace.hh"
24 
25 // primitive function instances
26 //
27 Bif_F12_TAKE      Bif_F12_TAKE     ::_fun;    // ↑
28 Bif_F12_DROP      Bif_F12_DROP     ::_fun;    // ↓
29 
30 // primitive function pointers
31 //
32 Bif_F12_TAKE      * Bif_F12_TAKE     ::fun = &Bif_F12_TAKE     ::_fun;
33 Bif_F12_DROP      * Bif_F12_DROP     ::fun = &Bif_F12_DROP     ::_fun;
34 
35 //=============================================================================
36 Value_P
first(Value_P B)37 Bif_F12_TAKE::first(Value_P B)
38 {
39 const Cell & first_B = B->get_ravel(0);
40    if (B->element_count() == 0)   // empty value: return prototype
41       {
42         if (first_B.is_lval_cell())   // (
43             {
44               Value_P Z(LOC);
45               Z->next_ravel()->init(first_B, B.getref(), LOC);
46               return Z;
47             }
48         Value_P Z = B->prototype(LOC);
49         Z->check_value(LOC);
50         return Z;
51       }
52 
53    if (!first_B.is_pointer_cell())   // simple cell
54       {
55         Value_P Z(LOC);
56         Z->get_ravel(0).init(first_B, Z.getref(), LOC);
57         Z->check_value(LOC);
58         return Z;
59       }
60 
61 Value_P v1 = first_B.get_pointer_value();
62 Value * v1_owner = v1->get_lval_cellowner();
63    if (v1_owner)   // B is a left value
64       {
65         Value_P B1(LOC);
66         new (&B1->get_ravel(0))   PointerCell(v1.get(), B1.getref());
67 
68         Value_P Z(LOC);
69         new (&Z->get_ravel(0))   LvalCell(&B1->get_ravel(0), v1_owner);
70 
71         Z->check_value(LOC);
72         return Z;
73       }
74    else
75       {
76         const ShapeItem ec = v1->element_count();
77         Value_P Z(v1->get_shape(), LOC);
78         if (ec == 0)   Z->get_ravel(0).init(v1->get_ravel(0), Z.getref(), LOC);
79 
80         loop(e, ec)   Z->next_ravel()->init(v1->get_ravel(e), Z.getref(), LOC);
81 
82         Z->check_value(LOC);
83         return Z;
84       }
85 }
86 //-----------------------------------------------------------------------------
87 Token
eval_AXB(Value_P A,Value_P X,Value_P B)88 Bif_F12_TAKE::eval_AXB(Value_P A, Value_P X, Value_P B)
89 {
90    if (X->element_count() == 0)   // no axes
91       {
92         Token result(TOK_APL_VALUE1, B->clone(LOC));
93         return result;
94       }
95 
96    // A↑[X]B ←→ ⊃[X](⊂A)↑¨⊂[X]B
97    //
98 Value_P cA = Bif_F12_PARTITION::fun->do_eval_B(A);        // ⊂A
99 Value_P cB = Bif_F12_PARTITION::fun->do_eval_XB(X, B);    // ⊂[X]B
100 Token take(TOK_FUN2, Bif_F12_TAKE::fun);
101 Token cT = Bif_OPER1_EACH::fun->eval_ALB(cA, take, cB);   // cA↑¨cB
102 
103 Token result = Bif_F12_PICK::fun->eval_XB(X, cT.get_apl_val());
104    return result;
105 }
106 //-----------------------------------------------------------------------------
107 Token
eval_AB(Value_P A,Value_P B)108 Bif_F12_TAKE::eval_AB(Value_P A, Value_P B)
109 {
110 Shape ravel_A1(A.get(), /* ⎕IO */ 0);   // checks that 1 ≤ ⍴⍴A and ⍴A ≤ MAX_RANK
111 
112    if (B->is_scalar())
113       {
114         Shape shape_B1;
115         loop(a, ravel_A1.get_rank())   shape_B1.add_shape_item(1);
116         Value_P B1 = B->clone(LOC);
117         B1->set_shape(shape_B1);
118         return Token(TOK_APL_VALUE1, do_take(ravel_A1, B1));
119       }
120    else
121       {
122         if (ravel_A1.get_rank() != B->get_rank())   LENGTH_ERROR;
123         return Token(TOK_APL_VALUE1, do_take(ravel_A1, B));
124       }
125 }
126 //-----------------------------------------------------------------------------
127 Value_P
do_take(const Shape & ravel_A1,Value_P B)128 Bif_F12_TAKE::do_take(const Shape & ravel_A1, Value_P B)
129 {
130    // ravel_A1 can have negative items (for take from the end).
131    //
132 Value_P Z(ravel_A1.abs(), LOC);
133 
134    if (ravel_A1.is_empty())   Z->set_default(*B.get(), LOC); // empty Z
135    else                       fill(ravel_A1, &Z->get_ravel(0), Z.getref(), B);
136    Z->check_value(LOC);
137    return Z;
138 }
139 //-----------------------------------------------------------------------------
140 void
fill(const Shape & shape_Zi,Cell * cZ,Value & Z_owner,Value_P B)141 Bif_F12_TAKE::fill(const Shape & shape_Zi, Cell * cZ, Value & Z_owner,
142                    Value_P B)
143 {
144    for (TakeDropIterator i(true, shape_Zi, B->get_shape()); i.more(); ++i)
145       {
146         const ShapeItem offset = i();
147         if (offset == -1)   cZ++->init_type(B->get_ravel(0), Z_owner, LOC);
148         else                cZ++->init(B->get_ravel(offset), Z_owner, LOC);
149       }
150 }
151 //=============================================================================
152 Token
eval_AB(Value_P A,Value_P B)153 Bif_F12_DROP::eval_AB(Value_P A, Value_P B)
154 {
155 Shape ravel_A(A.get(), /* ⎕IO */ 0);
156    if (A->get_rank() > 1)   RANK_ERROR;
157 
158    if (B->is_scalar())
159       {
160         // if B is a scalar then the result rank shall be the length of A->
161         // the result may be empty (shape 0 0 ... 0) if we drop something
162         // or non-empty (shape 1 1 ... 1) if we drop nothing.
163         //
164         const ShapeItem len_Z = ravel_A.get_volume() ? 0 : 1;
165 
166         Shape shape_Z;
167         loop(r, ravel_A.get_rank())   shape_Z.add_shape_item(len_Z);
168 
169         Value_P Z(shape_Z, LOC);
170 
171         Z->get_ravel(0).init(B->get_ravel(0), Z.getref(), LOC);
172         Z->check_value(LOC);
173         return Token(TOK_APL_VALUE1, Z);
174       }
175 
176    if (ravel_A.get_rank() == 0)   ravel_A.add_shape_item(1);   // A = ,A
177 
178    if (ravel_A.get_rank() != B->get_rank())   LENGTH_ERROR;
179 
180 Shape sh_Z;
181    loop(r, ravel_A.get_rank())
182        {
183          const ShapeItem sA = ravel_A.get_shape_item(r);
184          const ShapeItem sB = B->get_shape_item(r);
185          const ShapeItem pA = sA < 0 ? -sA : sA;
186          if (pA >= sB)   sh_Z.add_shape_item(0);   // over-drop
187          else            sh_Z.add_shape_item(sB - pA);
188        }
189 
190 Value_P Z(sh_Z, LOC);
191    if (sh_Z.is_empty())   // empty Z, e.g. from overdrop
192       {
193         Value_P Z(sh_Z, LOC);
194         Z->set_default(*B.get(), LOC);
195         Z->check_value(LOC);
196         return Token(TOK_APL_VALUE1, Z);
197       }
198 
199    for (TakeDropIterator i(false, ravel_A, B->get_shape()); i.more(); ++i)
200       {
201         const ShapeItem offset = i();
202         Z->next_ravel()->init(B->get_ravel(offset), Z.getref(), LOC);
203       }
204 
205    Z->check_value(LOC);
206    return Token(TOK_APL_VALUE1, Z);
207 }
208 //-----------------------------------------------------------------------------
209 Token
eval_AXB(Value_P A,Value_P X,Value_P B)210 Bif_F12_DROP::eval_AXB(Value_P A, Value_P X, Value_P B)
211 {
212    if (X->element_count() == 0)   // no axes
213       {
214         Token result(TOK_APL_VALUE1, B->clone(LOC));
215         return result;
216       }
217 
218    if (X->get_rank() > 1)    INDEX_ERROR;
219 
220 const uint64_t len_X = X->element_count();
221    if (len_X > MAX_RANK)     INDEX_ERROR;
222    if (len_X == 0)           INDEX_ERROR;
223 
224    if (A->get_rank() > 1)    RANK_ERROR;
225 
226 uint64_t len_A = A->element_count();
227    if (len_A != len_X)   LENGTH_ERROR;
228 
229 const APL_Integer qio = Workspace::get_IO();
230 
231    // init ravel_A = shape_B and seen.
232    //
233 Shape ravel_A(B->get_shape());
234 bool seen[MAX_RANK];
235    loop(r, B->get_rank())   seen[r] = false;
236 
237    loop(r, len_X)
238        {
239          const APL_Integer a = A->get_ravel(r).get_near_int();
240          const APL_Integer x = X->get_ravel(r).get_near_int() - qio;
241 
242          if (x >= B->get_rank())   INDEX_ERROR;
243          if (seen[x])              INDEX_ERROR;
244          seen[x] = true;
245 
246          const ShapeItem amax = B->get_shape_item(x);
247          if      (a >= amax)   ravel_A.set_shape_item(x, 0);
248          else if (a >= 0)      ravel_A.set_shape_item(x, a - amax);
249          else if (a > -amax)   ravel_A.set_shape_item(x, amax + a);
250          else                  ravel_A.set_shape_item(x, 0);
251        }
252 
253    return Token(TOK_APL_VALUE1, Bif_F12_TAKE::do_take(ravel_A, B));
254 }
255 //=============================================================================
256 
257