function hashComplex($val) { $hasher = hash_init('sha256'); if (is_array($val)) { hash_update($hasher, '['); // Ensure array is sorted by keys ksort($val); foreach ($val as $name => $v) { hash_update($hasher, $name); hash_update($hasher, '=>'); hash_update($hasher, hashComplex($v)); } hash_update($hasher, ']'); } else { if (is_gla($val)) { hash_update($hasher, 'gla'); hash_update($hasher, $val->hash()); } else { if (is_gf($val)) { hash_update($hasher, 'gf'); hash_update($hasher, $val->hash()); } else { if (is_gt($val)) { hash_update($hasher, 'gt'); hash_update($hasher, $val->hash()); } else { if (is_gist($val)) { hash_update($hasher, 'gist'); hash_update($hasher, $val->hash()); } else { if (is_gi($val)) { hash_update($hasher, 'gi'); hash_update($hasher, $val->hash()); } else { if (is_datatype($val)) { hash_update($hasher, 'datatype'); hash_update($hasher, $val->hash()); } else { if (is_functor($val)) { hash_update($hasher, 'functor'); hash_update($hasher, $val->hash()); } else { if (is_identifier($val)) { hash_update($hasher, 'identifier'); hash_update($hasher, $val->hash()); } else { if (is_attribute($val)) { hash_update($hasher, 'attribute'); hash_update($hasher, strval($val)); } else { if (is_int($val) || is_float($val) || is_string($val) || is_bool($val)) { hash_update($hasher, gettype($val)); hash_update($hasher, $val); } else { if (is_null($val)) { hash_update($hasher, 'null'); } else { $valType = is_object($val) ? get_class($val) : gettype($val); grokit_logic_error('Unable to hash unknown type ' . $valType); } } } } } } } } } } } } return hash_final($hasher); }
function OrderBy(array $t_args, array $inputs, array $outputs) { if (\count($inputs) == 0) { grokit_assert(array_key_exists('input', $t_args), 'No inputs given for OrderBy'); $inputs = $t_args['input']; foreach ($t_args['input'] as $name => &$type) { if (is_identifier($type)) { $type = lookupType(strval($type)); } grokit_assert(is_datatype($type), 'Invalid type given for input ' . $name); } } grokit_assert(array_key_exists('order', $t_args), 'No ordering attributes given for OrderBy'); $ordering = $t_args['order']; $ascOpts = ['ASC', 'ASCENDING', '+', '>']; $descOpts = ['DESC', 'DESCENDING', 'DES', 'DSC', '-', '<']; $ascending = []; foreach ($ordering as $name => $order) { grokit_assert(array_key_exists($name, $inputs), 'Ordering attribute ' . $name . ' not present in input'); if (in_array_icase($order, $ascOpts)) { $ascending[$name] = true; } else { if (in_array_icase($order, $descOpts)) { $ascending[$name] = false; } else { grokit_error("Unknown ordering " . $order . " given for attribute " . $name); } } } $rankAtt = get_default($t_args, 'rank', null); grokit_assert(is_null($rankAtt) || is_attribute($rankAtt), 'Rank argument should be null or an attribute'); grokit_assert(is_null($rankAtt) || array_key_exists($rankAtt->name(), $outputs), 'Rank attribute does not exist in outputs'); if (!is_null($rankAtt) && is_null($outputs[$rankAtt->name()])) { $outputs[$rankAtt->name()] = lookupType('base::BIGINT'); } $outputPassthroughAtts = []; foreach ($outputs as $name => $type) { if (is_null($rankAtt) || $rankAtt->name() != $name) { $outputPassthroughAtts[$name] = $type; } } $outToIn = []; $nInputs = \count($inputs); reset($inputs); reset($outputPassthroughAtts); for ($i = 0; $i < $nInputs; $i++) { $outName = key($outputPassthroughAtts); $inName = key($inputs); $outToIn[$outName] = $inName; // Unify types $outputs[$outName] = $inputs[$inName]; $outputPassthroughAtts[$outName] = $inputs[$inName]; next($inputs); next($outputPassthroughAtts); } $orderAtts = []; $extraAtts = []; foreach ($inputs as $name => $type) { if (array_key_exists($name, $ordering)) { $orderAtts[$name] = $type; } else { $extraAtts[$name] = $type; } } // Give 2^32 as the default, which should be effectively infinite $limitDefault = pow(2, 32); $limit = get_default($t_args, 'limit', $limitDefault); $limit = $limit == 0 ? $limitDefault : $limit; grokit_assert($limit > 0, 'The OrderBy limit must be a positive integer'); $className = generate_name('OrderBy'); $debug = get_default($t_args, 'debug', 0); ?> class <?php echo $className; ?> { struct Tuple { <?php foreach ($inputs as $name => $type) { ?> <?php echo $type; ?> <?php echo $name; ?> ; <?php } ?> Tuple( void ) = default; Tuple( const Tuple & other ) = default; Tuple( <?php echo array_template('const {val} & _{key}', ', ', $inputs); ?> ): <?php echo array_template('{key}(_{key})', ', ', $inputs); ?> { } Tuple & operator = (const Tuple & other ) = default; bool operator > ( const Tuple & other ) const { <?php foreach ($orderAtts as $name => $type) { $op1 = $ascending[$name] ? '<' : '>'; $op2 = !$ascending[$name] ? '<' : '>'; ?> if( <?php echo $name; ?> <?php echo $op1; ?> other.<?php echo $name; ?> ) return true; else if( <?php echo $name; ?> <?php echo $op2; ?> other.<?php echo $name; ?> ) return false; <?php } ?> return false; } bool operator < ( const Tuple& other ) const { return other > *this; } bool operator <= (const Tuple & other ) const { return ! (*this > other ); } bool operator >= (const Tuple & other ) const { return !( other > *this ); } <?php if ($debug > 0) { ?> std::string toString(void) const { std::ostringstream ss; ss << "( "; // > <?php $first = true; foreach ($inputs as $name => $type) { if ($first) { $first = false; } else { echo ' ss << ", ";' . PHP_EOL; } ?> ss << <?php echo $name; ?> ; // > <?php } // foreach input ?> ss << " )"; // > return ss.str(); } <?php } // debug > 0 ?> }; // struct Tuple typedef std::vector<Tuple> TupleVector; public: class Iterator { public: typedef TupleVector::const_iterator iter_type; private: iter_type begin; iter_type curr; iter_type end; public: Iterator(void) = default; Iterator( const iter_type & _begin, const iter_type & _end ) : begin(_begin), curr(_begin), end(_end) { } bool GetNextResult(<?php echo typed_ref_args($outputs); ?> ) { if( curr != end ) { <?php foreach ($outputPassthroughAtts as $name => $type) { ?> <?php echo $name; ?> = curr-><?php echo $outToIn[$name]; ?> ; <?php } if (!is_null($rankAtt)) { ?> <?php echo $rankAtt; ?> = (curr - begin) + 1; <?php } // if we need to output the rank ?> curr++; return true; } else { return false; } } }; private: uintmax_t __count; // number of tuples covered // K, as in Top-K static constexpr size_t K = <?php echo $limit; ?> ; TupleVector tuples; // Iterator for multi output type Iterator multiIterator; typedef std::greater<Tuple> TupleCompare; // Function to force sorting so that GetNext gets the tuples in order. void Sort(void) { TupleCompare comp; // If tuples doesn't contain at least K elements, it was never made into // a heap in the first place, so sort it normally. if( tuples.size() >= K ) { std::sort_heap(tuples.begin(), tuples.end(), comp); } else { std::sort(tuples.begin(), tuples.end(), comp); } } // Internal function to add a tuple to the heap void AddTupleInternal(Tuple & t ) { <?php if ($debug >= 1) { ?> { std::ostringstream ss; ss << "T ACK: " << t.toString() << std::endl; // > std::cerr << ss.str(); // > } <?php } ?> TupleCompare comp; if( tuples.size() >= K ) { <?php if ($debug >= 1) { ?> { std::ostringstream ss; ss << "T REP: " << tuples.front().toString() << std::endl; // > std::cerr << ss.str(); // > } <?php } ?> std::pop_heap(tuples.begin(), tuples.end(), comp); tuples.pop_back(); tuples.push_back(t); std::push_heap(tuples.begin(), tuples.end(), comp); } else { tuples.push_back(t); if( tuples.size() == K ) { std::make_heap(tuples.begin(), tuples.end(), comp); } } } public: <?php echo $className; ?> () : __count(0), tuples(), multiIterator() { } ~<?php echo $className; ?> () { } void AddItem(<?php echo const_typed_ref_args($inputs); ?> ) { __count++; Tuple t(<?php echo args($inputs); ?> ); <?php if ($debug >= 2) { ?> { std::ostringstream ss; ss << "T NEW: " << t.toString() << std::endl; // > std::cerr << ss.str(); // > } <?php } ?> if( tuples.size() == K && !(t > tuples.front()) ) return; AddTupleInternal(t); } void AddState( <?php echo $className; ?> & other ) { __count += other.__count; for( Tuple & el : other.tuples ) { if( tuples.size() < K /*>*/ || el > tuples.front() ) { AddTupleInternal(el); } } } void Finalize() { Sort(); Iterator::iter_type begin = tuples.cbegin(); Iterator::iter_type end = tuples.cend(); multiIterator = Iterator(begin, end); <?php if ($debug >= 1) { ?> std::ostringstream ss; ss << "[ "; //> bool first = true; for( auto el : tuples ) { if( first ) first = false; else ss << ", "; //>> ss << el.toString(); //>> } ss << " ]" << std::endl; // > std::cerr << ss.str(); //>> <?php } ?> } bool GetNextResult( <?php echo typed_ref_args($outputs); ?> ) { return multiIterator.GetNextResult(<?php echo args($outputs); ?> ); } }; <?php $system_headers = ['vector', 'algorithm', 'cinttypes']; if ($debug > 0) { $system_headers = array_merge($system_headers, ['iostream', 'sstream', 'string']); } return array('kind' => 'GLA', 'name' => $className, 'input' => $inputs, 'output' => $outputs, 'result_type' => 'multi', 'system_headers' => $system_headers); }