1 /*
2 
3  HyPhy - Hypothesis Testing Using Phylogenies.
4 
5  Copyright (C) 1997-now
6  Core Developers:
7  Sergei L Kosakovsky Pond (sergeilkp@icloud.com)
8  Art FY Poon    (apoon42@uwo.ca)
9  Steven Weaver (sweaver@temple.edu)
10 
11  Module Developers:
12  Lance Hepler (nlhepler@gmail.com)
13  Martin Smith (martin.audacis@gmail.com)
14 
15  Significant contributions from:
16  Spencer V Muse (muse@stat.ncsu.edu)
17  Simon DW Frost (sdf22@cam.ac.uk)
18 
19  Permission is hereby granted, free of charge, to any person obtaining a
20  copy of this software and associated documentation files (the
21  "Software"), to deal in the Software without restriction, including
22  without limitation the rights to use, copy, modify, merge, publish,
23  distribute, sublicense, and/or sell copies of the Software, and to
24  permit persons to whom the Software is furnished to do so, subject to
25  the following conditions:
26 
27  The above copyright notice and this permission notice shall be included
28  in all copies or substantial portions of the Software.
29 
30  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
31  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
33  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
34  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
35  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
36  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 
38  */
39 
40 /*#include <stdlib.h>
41 #include <string.h>
42 #include <stdio.h>
43 #include <time.h>
44 #include <float.h>
45 #include <math.h>
46 #include <limits.h>
47 
48 #include "batchlan.h"
49 #include "polynoml.h"
50 #include "likefunc.h"
51 
52 #include "avllist.h"
53 #include "avllistx.h"
54 #include "avllistxl.h"
55 
56 #include "function_templates.h"
57 #include "mersenne_twister.h"
58 #include "global_things.h"
59 #include "string_file_wrapper.h"*/
60 
61 #include "global_things.h"
62 #include "global_object_lists.h"
63 #include "associative_list.h"
64 #include "batchlan.h"
65 #include "avllistxl_iterator.h"
66 
67 
68 using namespace hy_global;
69 using namespace hyphy_global_objects;
70 
71 #define         kAssociativeListDefaultReturn (new _Constant (0.0));
72 
73 
74 
75 
76 //_____________________________________________________________________________________________
77 // AssociativeList
78 //_____________________________________________________________________________________________
79 
_AssociativeList(void)80 _AssociativeList::_AssociativeList (void):avl(&theData) {
81 }
82 
83 //_____________________________________________________________________________________________
84 
makeDynamic(void) const85 BaseRef _AssociativeList::makeDynamic (void) const {
86     _AssociativeList * newAL = new _AssociativeList ();
87     newAL->Duplicate (this);
88     return newAL;
89 }
90 
91 
92 //bool _debug_memory_leak = false;
93 
94 //_____________________________________________________________________________________________
95 
ParseStringRepresentation(_String & serialized_form,_FormulaParsingContext & fpc)96 bool _AssociativeList::ParseStringRepresentation (_String& serialized_form, _FormulaParsingContext& fpc ) {
97     _List               splitKeys;
98     bool                doErrors = fpc.errMsg() == nil,
99                         compute_keys_values = fpc.buildComplexObjects();
100    _VariableContainer const* theP = fpc.formulaScope();
101 
102     _ElementaryCommand::ExtractConditions (serialized_form, 0, splitKeys, ',' , false);
103 
104     try {
105         for (unsigned long k = 0UL; k < splitKeys.countitems(); k ++) {
106             _List key_value_pair;
107             _ElementaryCommand::ExtractConditions (*(_String*)splitKeys(k), 0, key_value_pair, ':' , false);
108             if (key_value_pair.countitems() == 2UL) {
109 
110                 //_debug_memory_leak = true;
111                 _String  key        (compute_keys_values ? ProcessLiteralArgument((_String*)key_value_pair(0),theP) : *(_String*)key_value_pair(0));
112                 //_debug_memory_leak = false;
113 
114                 if (key.empty()) {
115                   key = *(_String*)key_value_pair(0);
116                 }
117 
118                 HBLObjectRef   valueC = nil;
119                 if (compute_keys_values) {
120                     _Formula value      (*(_String*)key_value_pair(1),theP, doErrors?nil :fpc.errMsg());
121                     valueC  = value.Compute();
122                     if (valueC) {
123                         valueC->AddAReference();
124                     }
125                 } else {
126                     valueC  =  new _MathObject;
127                 }
128 
129                 if (valueC) {
130                     MStore (key, valueC, false);
131                 } else {
132                     throw (((_String*)key_value_pair(1))->Enquote() & " could not be evaluated");
133 
134                 }
135             } else {
136                 throw (((_String*)splitKeys(k))->Enquote() & " does not appear to specify a valid key:value pair");
137             }
138         }
139     } catch (const _String& err) {
140         if (doErrors) {
141             HandleApplicationError(err);
142         }
143         return false;
144     }
145     return true;
146 }
147 
148 //_____________________________________________________________________________________________
149 
toStr(unsigned long padding)150 BaseRef _AssociativeList::toStr (unsigned long padding) {
151     return Serialize  (padding);
152 }
153 
154 //_____________________________________________________________________________________________
155 
Duplicate(BaseRefConst br)156 void _AssociativeList::Duplicate (BaseRefConst br) {
157     if (!SingleReference ()) {
158         HandleApplicationError(_String (__PRETTY_FUNCTION__).Enquote() & " called from an object with multiple references");
159     }
160     _AssociativeList const * copyMe = (_AssociativeList const*)br;
161     theData.Duplicate (&copyMe->theData);
162     avl.leftChild.Duplicate (&copyMe->avl.leftChild);
163     avl.rightChild.Duplicate (&copyMe->avl.rightChild);
164     avl.balanceFactor.Duplicate (&copyMe->avl.balanceFactor);
165     avl.emptySlots.Duplicate (&copyMe->avl.emptySlots);
166     avl.xtraD.Duplicate (&copyMe->avl.xtraD);
167     avl.root = copyMe->avl.root;
168 }
169 
170 //_____________________________________________________________________________________________
171 
MCoord(HBLObjectRef p,HBLObjectRef cache)172 HBLObjectRef _AssociativeList::MCoord (HBLObjectRef p, HBLObjectRef cache) {
173     if (cache && cache->ObjectClass() == STRING) {
174         return ((_FString*)cache)->UpdatePayload ((_String*)p->toStr());
175     }
176     return new _FString ((_String*)p->toStr());
177 }
178 
179 //_____________________________________________________________________________________________
MAccess(HBLObjectRef p,HBLObjectRef cache)180 HBLObjectRef _AssociativeList::MAccess (HBLObjectRef p, HBLObjectRef cache) {
181     long        f;
182 
183     if (p->ObjectClass() == STRING) {
184         f = avl.Find (&((_FString*)p)->get_str());
185     } else {
186         _String s ((_String*)p->toStr());
187         f = avl.Find (&s);
188     }
189     if (f>=0L) {
190         HBLObjectRef res = (HBLObjectRef)avl.GetXtra (f);
191         if (cache != res)
192             res->AddAReference();
193         return res;
194     } else {
195         return kAssociativeListDefaultReturn;
196     }
197 }
198 
199 //_____________________________________________________________________________________________
Equal(HBLObjectRef p)200 bool _AssociativeList::Equal (HBLObjectRef p) {
201     if (p->ObjectClass() == ASSOCIATIVE_LIST) {
202         _AssociativeList * rhs = (_AssociativeList*) p;
203         if (countitems() == rhs->countitems()) {
204             for (AVLListXLIteratorKeyValue key_value : AVLListXLIterator (&avl)) {
205                 _String const* my_key = key_value.get_key();
206                 HBLObjectRef my_object = (HBLObjectRef)key_value.get_object();
207                 HBLObjectRef rhs_object = rhs->GetByKey(*my_key, my_object->ObjectClass());
208                 if (!rhs_object || !my_object->Equal(rhs_object)) {
209                         return false;
210                 }
211             }
212             return true;
213         }
214     }
215     return false;
216 }
217 
218 //_____________________________________________________________________________________________
Random(HBLObjectRef p,HBLObjectRef cache)219 HBLObjectRef _AssociativeList::Random (HBLObjectRef p, HBLObjectRef cache) {
220     bool with_replacement = false;
221 
222     try {
223         if (p->ObjectClass() == NUMBER) {
224             _AssociativeList * result;
225             if (cache && cache->ObjectClass() == ASSOCIATIVE_LIST) {
226                 result = (_AssociativeList*)cache;
227                 result->Clear();
228             } else {
229                 result = new _AssociativeList;
230             }
231             with_replacement = !CheckEqual(0.0, p->Compute()->Value());
232 
233             //unsigned long items = countitems();
234             _SimpleList  reshuffled (countitems(), 0, 1);
235             if (with_replacement) {
236                 reshuffled.PermuteWithReplacement(1);
237             } else {
238                 reshuffled.Permute(1);
239             }
240 
241             _List copied_values;
242 
243             for (AVLListXLIteratorKeyValue key_value : AVLListXLIterator (&avl)) {
244                 copied_values << key_value.get_object();
245             }
246 
247             long index = 0L;
248             for (AVLListXLIteratorKeyValue key_value : AVLListXLIterator (&avl)) {
249                 result->MStore (*(_String*)avl.Retrieve(key_value.get_index()),
250                                 (HBLObjectRef)copied_values.GetItem(reshuffled.get (index++)),
251                                 true
252                                );
253             }
254 
255             return result;
256 
257         } else {
258             throw (_String ("Unsupported argument type"));
259         }
260     } catch (const _String& err) {
261         HandleApplicationError (err);
262     }
263     return new _MathObject;
264 }
265 
266 //_____________________________________________________________________________________________
MIterator(HBLObjectRef p,HBLObjectRef p2,HBLObjectRef cache)267 HBLObjectRef _AssociativeList::MIterator (HBLObjectRef p, HBLObjectRef p2, HBLObjectRef cache) {
268 
269     const _String     kAVLIteratorOrder          = "INDEXORDER",
270                       kAVLIteratorOrderValue     = "VALUEINDEXORDER";
271 
272     long done = 0;
273 
274     _List reference_manager;
275 
276     try {
277 
278         if (p->ObjectClass() == STRING && p2->ObjectClass() == STRING) {
279 
280             long avlRoot = avl.GetRoot();
281 
282             if (avlRoot >= 0) {
283 
284                 _String * callback_id  = (_String*)p->toStr(),
285                         * filter_id    = (_String*)p2->toStr();
286 
287                 reference_manager < callback_id < filter_id;
288 
289                 long    callback  = FindBFFunctionName (*callback_id),
290                         filter    = FindBFFunctionName (*filter_id);
291 
292                 if (callback < 0L || GetBFFunctionArgumentCount(callback) != 2L) {
293                     throw (_String("The first argument in an iterator call for Associative Arrays must be a valid identifier of a function taking two arguments (key, value)"));
294                 } else {
295                     if (filter >= 0L && GetBFFunctionArgumentCount (filter) != 1L) {
296                         throw (_String("The second argument in an iterator call for Associative Arrays must be either empty or a valid identifier of a function taking a single argument"));
297                     }
298 
299                     _Formula      testFormula,
300                                   actionFormula;
301 
302                     actionFormula.GetList() < new _Operation()
303                                             < new _Operation()
304                                             < new _Operation(kEmptyString,-callback-1L);
305 
306 
307                     if (filter >= 0L) {
308                         testFormula.GetList() < new _Operation() < new _Operation(kEmptyString,-filter-1L);
309                     }
310 
311                     _FString * fKey = new _FString;
312                     // TODO SLKP 20181113 : this could be a memory leak, but it needs to be allocated dynamically
313                     // otherwise if "actionFormula" makes use of "theKey" it may not be properly cleared
314                     for (AVLListXLIteratorKeyValue filter_key_value : AVLListXLIterator (&avl)) {
315                         _String * current_key = (_String *)avl.Retrieve (filter_key_value.get_index());
316                         if (current_key) {
317                             fKey->SetStringContent (new _StringBuffer (*current_key));
318                             if (filter >= 0L) {
319                                 testFormula.GetIthTerm(0)->SetNumber(fKey);
320                                 if (CheckEqual(testFormula.Compute()->Value(),0.0)) {
321                                     continue;
322                                 }
323                             }
324                             actionFormula.GetIthTerm(0)->SetNumber(fKey);
325                             actionFormula.GetIthTerm(1)->SetNumber((HBLObjectRef)filter_key_value.get_object());
326                             //StringToConsole((_String*)actionFormula.toStr(kFormulaStringConversionNormal)); NLToConsole();
327                             actionFormula.Compute();
328                             done ++;
329                         }
330                     }
331 
332                     actionFormula.GetIthTerm(0)->SetNumber(nil);
333                     actionFormula.GetIthTerm(1)->SetNumber(nil);
334                     if (filter >= 0L) {
335                         testFormula.GetIthTerm(0)->SetNumber(nil);
336                     }
337 
338                     DeleteObject (fKey);
339 
340                 }
341              }
342         } else if (p->ObjectClass () == STRING && p2->ObjectClass () == NUMBER) {
343             _String * mode  = (_String*)p->toStr();
344             reference_manager < mode;
345             HBLObjectRef result = nil;
346 
347             if (*mode == kAVLIteratorOrder|| *mode == kAVLIteratorOrderValue) {
348                 long index = avl.GetByIndex(p2->Compute()->Value());
349 
350                 if (index >= 0L) {
351                   result = (*mode == kAVLIteratorOrder )? (new _FString(*(_String*)avl.Retrieve(index),false)): ((HBLObjectRef)avl.GetXtra (index)->makeDynamic());
352                 } else {
353                   throw _String("Index out of bounds in call to AVL iterator (by index)");
354                 }
355             }
356 
357             if (result) {
358               return result;
359             }
360         } else {
361             throw _String("Both arguments must be Strings (or a String Literal and a number) in an iterator call for Associative Arrays");
362         }
363     } catch (const _String& err) {
364         HandleApplicationError (err);
365     }
366     return _returnConstantOrUseCache(done, cache);
367 }
368 
369 //_____________________________________________________________________________________________
GetByKey(_String const & key,long objType) const370 HBLObjectRef _AssociativeList::GetByKey (_String const& key, long objType) const {
371     HBLObjectRef res = GetByKey(key);
372     if (res && ((res->ObjectClass() & objType) > 0L)) {
373       return res;
374     }
375     return nil;
376 }
377 
378 //_____________________________________________________________________________________________
GetByKeyException(_String const & key,long objType) const379 HBLObjectRef _AssociativeList::GetByKeyException (_String const& key, long objType) const {
380     HBLObjectRef res = GetByKey(key, objType);
381     if (res) {
382         return res;
383     }
384     throw key.Enquote() & " was not associated with a value of type " & FetchObjectNameFromType (objType).Enquote();
385 }
386 
387 
388 //_____________________________________________________________________________________________
389 
GetNumberByKey(_String const & key) const390 hyFloat _AssociativeList::GetNumberByKey (_String const& key) const {
391     _Constant * c = (_Constant*)GetByKey(key, NUMBER);
392     if (c) {
393         return c->Value();
394     }
395     throw key.Enquote() & " was not associated with a numeric value";
396 }
397 
398 //_____________________________________________________________________________________________
GetByKey(_String const & key) const399 HBLObjectRef _AssociativeList::GetByKey (_String const& key) const {
400     return (HBLObjectRef)avl.GetDataByKey(&key);
401 }
402 
403 //_____________________________________________________________________________________________
GetByKey(long nKey,long objType) const404 HBLObjectRef _AssociativeList::GetByKey (long nKey, long objType) const {
405     return GetByKey (_String(nKey), objType);
406 }
407 
408   //_____________________________________________________________________________________________
Clear(void)409 void _AssociativeList::Clear (void) {
410   avl.Clear(true);
411 }
412 
413 
414 //_____________________________________________________________________________________________
DeleteByKey(HBLObjectRef p)415 void _AssociativeList::DeleteByKey (HBLObjectRef p) {
416     if (p->ObjectClass() == STRING) {
417         avl.Delete (&((_FString*)p)->get_str(),true);
418     } else {
419         if (p->ObjectClass() == ASSOCIATIVE_LIST) {
420             _List * keys2remove = ((_AssociativeList*)p)->GetKeys();
421             for (long ki = 0; ki < keys2remove->lLength; ki++) {
422                 avl.Delete ((_String*)(*keys2remove)(ki),true);
423             }
424             DeleteObject (keys2remove);
425         } else {
426             _String * s = (_String*)p->toStr();
427             avl.Delete (s,true);
428             DeleteObject (s);
429         }
430     }
431 }
432 
433 //_____________________________________________________________________________________________
DeleteByKey(_String const & key)434 void _AssociativeList::DeleteByKey (_String const& key) {
435     avl.Delete (&key,true);
436 }
437 
438 
439 //_____________________________________________________________________________________________
MStore(_String * p,HBLObjectRef inObject,bool repl,long opCode)440 bool _AssociativeList::MStore (_String * p, HBLObjectRef inObject, bool repl, long opCode) {
441     if (!p) {
442         return false;
443     }
444 
445     long       f     = avl.Find (p);
446 
447     if (f>=0) { // already exists - replace
448         if (opCode == HY_OP_CODE_ADD) {
449             _List arguments;
450             arguments << inObject;
451 
452             HBLObjectRef newObject = ((HBLObjectRef)avl.GetXtra(f))->ExecuteSingleOp(HY_OP_CODE_ADD,&arguments);
453             if (repl == false) {
454                 DeleteObject (inObject);
455             } else {
456                 repl = false;
457             }
458             inObject = newObject;
459         }
460         avl.xtraD.Replace (f, inObject, repl);
461         return false;
462     } else { // insert new
463         if (repl) {
464             BaseRef br = inObject->makeDynamic();
465             avl.Insert (p,(long)br,false);
466             //br->nInstances--;
467         } else {
468             avl.Insert (p,(long)inObject,false);
469         }
470         return true;
471     }
472 }
473 
474 //_____________________________________________________________________________________________
MStore(HBLObjectRef p,HBLObjectRef inObject,bool repl,long opCode)475 void _AssociativeList::MStore (HBLObjectRef p, HBLObjectRef inObject, bool repl, long opCode) {
476     _StringBuffer *sr = ((_FString*)p)->get_str_ref();
477     if (!MStore (sr, inObject, repl, opCode)) {
478         sr->RemoveAReference();
479     }
480 }
481 
482 //_____________________________________________________________________________________________
MStore(const _String & obj,HBLObjectRef inObject,bool repl)483 void _AssociativeList::MStore (const _String& obj, HBLObjectRef inObject, bool repl) {
484     _FString f (obj);
485     MStore (&f,inObject, repl);
486 }
487 
488 //_____________________________________________________________________________________________
489 
operator <<(_associative_list_key_value pair)490 _AssociativeList &  _AssociativeList:: operator <<     (_associative_list_key_value pair) {
491   if (pair.key) {
492     MStore (pair.key, pair.payload, true);
493   } else {
494     _String next_index ((long)Length());
495     MStore (next_index, pair.payload, true);
496   }
497   return *this;
498 }
499 
500 //_____________________________________________________________________________________________
501 
502 
operator <(_associative_list_key_value pair)503 _AssociativeList &  _AssociativeList:: operator <     (_associative_list_key_value pair) {
504   if (pair.key) {
505     MStore (pair.key, pair.payload, false);
506   } else {
507     _String next_index ((long)Length());
508     MStore (next_index, pair.payload, false);
509   }
510   return *this;
511 }
512 
513 
514 //_____________________________________________________________________________________________
MStore(const _String & obj,const _String & info)515 void _AssociativeList::MStore (const _String& obj, const _String& info) {
516     _FString *  inf = new _FString (info);
517     MStore (obj, inf, false);
518 }
519 
520 
521 //_____________________________________________________________________________________________
Serialize(unsigned long padding) const522 _StringBuffer * _AssociativeList::Serialize (unsigned long padding) const {
523 
524     _StringBuffer  * out_string = new _StringBuffer (1024L);
525     _String          padder (" ", padding);
526 
527 
528 
529     (*out_string) << "{";
530     bool        doComma = false;
531     _List * meKeys = GetKeys();
532     for (long k = 0L; k < meKeys->countitems(); k++) {
533         _String   *thisKey  = (_String*)(*meKeys)(k);
534         if (thisKey) {
535             if (doComma) {
536                 (*out_string) << ',';
537              }
538 
539             (*out_string) << '\n' << padder << ' ';
540 
541             (*out_string) << '"';
542             out_string->SanitizeAndAppend (*thisKey);
543             (*out_string) << '"';
544 
545             HBLObjectRef anObject = GetByKey (*thisKey);
546 
547             (*out_string) << ':';
548             if (anObject->ObjectClass() == STRING) {
549                 (*out_string) << '"';
550                 out_string->SanitizeAndAppend(_String ((_String*)anObject->toStr(padding+2)));
551                 (*out_string) << '"';
552             } else {
553                 if (anObject->ObjectClass() != HY_UNDEFINED) {
554                     out_string->AppendNewInstance((_String*)anObject->toStr(padding+2));
555                 } else {
556                     (*out_string) << kNullToken;
557                 }
558             }
559             doComma = true;
560         }
561     }
562 
563     DeleteObject (meKeys);
564 
565     (*out_string) << '\n' << padder << "}";
566     out_string->TrimSpace ();
567 
568     return out_string;
569 }
570 
571 
572 //__________________________________________________________________________________
Compute(void)573 HBLObjectRef _AssociativeList::Compute (void) {
574     return this;
575 }
576 
577 //__________________________________________________________________________________
GetKeys(void) const578 _List* _AssociativeList::GetKeys (void) const {
579 
580     _List * keys = new _List;
581 
582     for (AVLListXLIteratorKeyValue key_value : AVLListXLIterator (&avl)) {
583         (*keys) << avl.Retrieve(key_value.get_index());
584     }
585 
586     return keys;
587 
588 }
589 
590 //_____________________________________________________________________________________________
FillInList(_List & fill_me)591 void        _AssociativeList::FillInList (_List& fill_me) {
592 
593     unsigned long ll = fill_me.countitems();
594     // checkpoint the length of the list
595     try {
596       // try filling in in numerical order first 0, 1, ...
597       // failing that (if the keys are not numeric)
598       // fill in by alphanumeric keys
599       unsigned long  my_length = avl.countitems();
600 
601       for (long index = 0L; index < my_length; index++) {
602         _String key (index);
603         if (HBLObjectRef value = GetByKey (key)) {
604           fill_me.AppendNewInstance(value->toStr());
605         } else {
606           throw (1);
607         }
608       }
609     } catch (int e) {
610         while (fill_me.countitems () > ll) {
611           fill_me.Delete(fill_me.countitems ()-1);
612         }
613 
614         for (AVLListXLIteratorKeyValue key_value : AVLListXLIterator (&avl)) {
615             _String* aKey = (_String*)avl.Retrieve(key_value.get_index());
616             if (aKey) {
617                 fill_me.AppendNewInstance(key_value.get_object()->toStr());
618             }
619         }
620     }
621 }
622 
623 
624 //_____________________________________________________________________________________________
Merge(HBLObjectRef p)625 void        _AssociativeList::Merge (HBLObjectRef p) {
626     //SW20111207: I don't think we should ever have to worry about avl traversing
627     //here as long as the other methods are implemented properly
628 
629 
630     if(p==this){
631         return;
632     }
633 
634     if (p && p->ObjectClass() == ASSOCIATIVE_LIST) {
635      _AssociativeList *rhs = (_AssociativeList*) p;
636       if (rhs->avl.countitems()) {
637           for (AVLListXLIteratorKeyValue key_value : AVLListXLIterator (&rhs->avl)) {
638               MStore(*(_String*)rhs->avl.Retrieve (key_value.get_index()),(HBLObjectRef)key_value.get_object(),true);
639           }
640       }
641     }
642     else {
643         HandleApplicationError ("Associative list merge operation requires an associative list argument.");
644     }
645 }
646 
647   //_____________________________________________________________________________________________
ExtremeValue(bool do_mimimum,HBLObjectRef cache) const648 HBLObjectRef        _AssociativeList::ExtremeValue (bool do_mimimum, HBLObjectRef cache) const {
649   _String const * best_key = nil;
650   hyFloat best_value = do_mimimum ? INFINITY : -INFINITY;
651 
652   if (avl.countitems()) {
653       for (AVLListXLIteratorKeyValue key_value : AVLListXLIterator (&avl)) {
654           HBLObjectRef value = (HBLObjectRef)key_value.get_object();
655           switch (value->ObjectClass()){
656               case NUMBER:
657                   hyFloat number = ((_Constant*)value)->Value();
658                   if (do_mimimum) {
659                       if (number < best_value) {
660                           best_value = number;
661                           best_key   = (_String const*)avl.Retrieve (key_value.get_index());
662                       }
663                   } else {
664                       if (number > best_value) {
665                           best_value = number;
666                           best_key   = (_String const*)avl.Retrieve (key_value.get_index());
667                       }
668                   }
669                   break;
670           }
671       }
672   }
673 
674 
675   _AssociativeList * result = new _AssociativeList;
676   (*result) < _associative_list_key_value {"key", best_key ? new _FString (*best_key, false) : new _MathObject}
677             < _associative_list_key_value {"value", new _Constant (best_value)};
678   return result;
679 
680 }
681 
682 //_____________________________________________________________________________________________
Sum(HBLObjectRef cache)683 HBLObjectRef        _AssociativeList::Sum (HBLObjectRef cache) {
684     hyFloat sum = 0.;
685 
686     for (AVLListXLIteratorKeyValue key_value : AVLListXLIterator (&avl)) {
687         HBLObjectRef value = (HBLObjectRef)key_value.get_object();
688         switch (value->ObjectClass()){
689             case NUMBER:
690                 sum += ((_Constant*)value)->Value();
691                 break;
692             case STRING:
693                 sum += ((_FString*)value)->get_str().to_float();
694                 break;
695             case MATRIX: {
696                 _Constant * sumOfValue = (_Constant*) ((_Matrix*)value->Compute())->Sum(cache);
697                 sum += sumOfValue->Value();
698                 if (sumOfValue != cache) {
699                     DeleteObject (sumOfValue);
700                 }
701                 break;
702             }
703             case ASSOCIATIVE_LIST: {
704                 _Constant * sumOfValue = (_Constant*) ((_AssociativeList*)value->Compute())->Sum(cache);
705                 sum += sumOfValue->Value();
706                 if (sumOfValue != cache) {
707                     DeleteObject (sumOfValue);
708                 }
709                 break;
710             }
711         }
712     }
713 
714     return _returnConstantOrUseCache (sum, cache);
715 }
716 
717 //__________________________________________________________________________________
718 
719 
ExecuteSingleOp(long opCode,_List * arguments,_hyExecutionContext * context,HBLObjectRef cache)720 HBLObjectRef _AssociativeList::ExecuteSingleOp (long opCode, _List* arguments, _hyExecutionContext* context, HBLObjectRef cache)  {
721 
722   switch (opCode) {
723     case HY_OP_CODE_ABS:
724      return _returnConstantOrUseCache(Length(), cache);
725 
726     case HY_OP_CODE_EVAL:
727       return (HBLObjectRef) makeDynamic();
728 
729     case HY_OP_CODE_COLUMNS: {
730       // Columns -- get all unique values (as strings)
731       _List    unique_values_aux;
732       _AVLList unique_values (&unique_values_aux);
733 
734       for (unsigned long k=0UL; k<avl.dataList->lLength; k++) {
735         BaseRef anItem = ((BaseRef*)avl.dataList->list_data)[k];
736         if (anItem) {
737           _String* string_value = (_String*) avl.GetXtra(k)->toStr();
738           if (unique_values.Insert (string_value, 0L, false) < 0) {
739             DeleteObject(string_value);
740           }
741         }
742       }
743       unique_values.ReorderList();
744       return new _Matrix (*(_List*)unique_values.dataList);
745     }
746 
747     case HY_OP_CODE_ROWS: {
748       // Rows - get keys
749       if (avl.emptySlots.lLength) {
750         _List  dataListCompact;
751         for (long k=0; k<avl.dataList->lLength; k++) {
752           BaseRef anItem = ((BaseRef*)avl.dataList->list_data)[k];
753           if (anItem) {
754             dataListCompact << anItem;
755           }
756         }
757         return new _Matrix (dataListCompact);
758       }
759       return new _Matrix (*(_List*)avl.dataList);
760     }
761 
762     case HY_OP_CODE_TYPE: // Type
763       return Type(cache);
764 
765     case HY_OP_CODE_MAX: // Max
766       return ExtremeValue (false, cache);
767 
768     case HY_OP_CODE_MIN: // Max
769       return ExtremeValue (true, cache);
770 
771 
772 
773   }
774 
775   _MathObject * arg0 = _extract_argument (arguments, 0UL, false);
776 
777   switch (opCode) { // next check operations without arguments or with one argument
778     case HY_OP_CODE_ADD: // +
779       if (arg0) {
780         _String * new_key = new _String((long)avl.countitems());
781         if (!MStore (new_key, arg0, true)) {
782             DeleteObject (new_key);
783         }
784         return _returnConstantOrUseCache(avl.countitems(), cache);
785       }
786       return Sum (cache);
787   }
788 
789 
790   if (arg0) {
791     switch (opCode) { // operations that require exactly one argument
792        case HY_OP_CODE_EQ:
793         return _returnConstantOrUseCache (Equal(arg0), cache);
794 
795        case HY_OP_CODE_NEQ:
796         return _returnConstantOrUseCache (!Equal(arg0), cache);
797 
798       case HY_OP_CODE_MCOORD: // MCoord
799         return MCoord (arg0, cache);
800 
801       case HY_OP_CODE_MUL: // merge
802         Merge (arg0);
803         return _returnConstantOrUseCache (avl.countitems(), cache);
804 
805       case HY_OP_CODE_SUB:
806         DeleteByKey (arg0);
807          return _returnConstantOrUseCache (avl.countitems(), cache);
808 
809         case HY_OP_CODE_RANDOM:
810             return Random (arg0, cache);
811 
812         case HY_OP_CODE_DIV:
813 
814         if (arg0->ObjectClass () == STRING) {
815           if (avl.Find (&((_FString*)arg0)->get_str()) >= 0L) {
816             return _returnConstantOrUseCache (1., cache);
817           }
818         } else {
819           _String serialized ((_String*)arg0->toStr());
820           if (avl.Find (&serialized) >= 0L) {
821             return _returnConstantOrUseCache (1., cache);
822           }
823         }
824         return _returnConstantOrUseCache (0., cache);
825 
826     }
827     _MathObject * arg1 = _extract_argument (arguments, 1UL, false);
828 
829     switch (opCode) { //  check operations with 1 or 2 arguments
830       case HY_OP_CODE_MACCESS: // MAccess
831         if (arg1) {
832           return MIterator (arg0,arg1, cache);
833         } else {
834           return MAccess   (arg0, cache);
835         }
836     }
837   }
838 
839 
840   switch (opCode) {
841     case HY_OP_CODE_TYPE:
842     case HY_OP_CODE_ADD:
843     case HY_OP_CODE_MCOORD:
844     case HY_OP_CODE_MUL:
845     case HY_OP_CODE_SUB:
846     case HY_OP_CODE_MACCESS:
847     case HY_OP_CODE_DIV:
848       WarnWrongNumberOfArguments (this, opCode,context, arguments);
849       break;
850     default:
851       WarnNotDefined (this, opCode,context);
852   }
853 
854   return new _MathObject;
855 
856 }
857 
858 
859