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);
 }
Example #2
0
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);
}