text:
<?php

require_once(PHPMORPHY_DIR . '/gramtab.php');
require_once(PHPMORPHY_DIR . '/unicode.php');

// ----------------------------
// Morphier interface
// ----------------------------
interface phpMorphy_Morphier_Interface {
    function getAnnot($word);
    function getBaseForm($word);
    function getAllForms($word);
    function getPseudoRoot($word);
    function getPartOfSpeech($word);
    function getWordDescriptor($word);
    function getAllFormsWithAncodes($word);
    function getAncode($word);
    function getGrammarInfoMergeForms($word);
    function getGrammarInfo($word);
}

class phpMorphy_Morphier_Empty implements phpMorphy_Morphier_Interface {
    function getAnnot($word) { return false; }
    function getBaseForm($word) { return false; }
    function getAllForms($word) { return false; }
    function getAllFormsWithGramInfo($word) { return false; }
    function getPseudoRoot($word) { return false; }
    function getPartOfSpeech($word) { return false; }
    function getWordDescriptor($word) { return false; }
    function getAllFormsWithAncodes($word) { return false; }
    function getAncode($word) { return false; }
    function getGrammarInfoMergeForms($word) { return false; }
    function getGrammarInfo($word) { return false; }
    function castFormByGramInfo($word, $partOfSpeech, $grammems, $returnWords = false, $callback = null) { return false; }
}

// ----------------------------
// Annot decoder
// ----------------------------
interface phpMorphy_AnnotDecoder_Interface {
    function decode($annotsRaw, $withBase);
};

abstract class phpMorphy_AnnotDecoder_Base implements phpMorphy_AnnotDecoder_Interface {
    const INVALID_ANCODE_ID = 0xFFFF;
    protected
        $ends,
        $unpack_str,
        $block_size;

    function __construct($ends) {
        $this->ends = $ends;

        $this->unpack_str = $this->getUnpackString();
        $this->block_size = $this->getUnpackBlockSize();
    }

    abstract protected function getUnpackString();
    abstract protected function getUnpackBlockSize();

    function decode($annotRaw, $withBase) {
        if(empty($annotRaw)) {
            throw new phpMorphy_Exception("Empty annot given");
        }

        $unpack_str = $this->unpack_str;
        $unpack_size = $this->block_size;

        $result = unpack("Vcount/$unpack_str", $annotRaw);

        if(false === $result) {
            throw new phpMorphy_Exception("Invalid annot string '$annotRaw'");
        }

        if($result['common_ancode'] == self::INVALID_ANCODE_ID) {
            $result['common_ancode'] = null;
        }

        $count = $result['count'];

        $result = array($result);

        if($count > 1) {
            for($i = 0; $i < $count - 1; $i++) {
                $res = unpack($unpack_str, $GLOBALS['__phpmorphy_substr']($annotRaw, 4 + ($i + 1) * $unpack_size, $unpack_size));

                if($res['common_ancode'] == self::INVALID_ANCODE_ID) {
                    $res['common_ancode'] = null;
                }

                $result[] = $res;
            }
        }

        if($withBase) {
            $items = explode($this->ends, $GLOBALS['__phpmorphy_substr']($annotRaw, 4 + $count * $unpack_size));
            for($i = 0; $i < $count; $i++) {
                $result[$i]['base_prefix'] = $items[$i * 2];
                $result[$i]['base_suffix'] = $items[$i * 2 + 1];
            }
        }

        return $result;
    }
}

class phpMorphy_AnnotDecoder_Common extends phpMorphy_AnnotDecoder_Base {
    protected function getUnpackString() {
        return 'Voffset/vcplen/vplen/vflen/vcommon_ancode/vforms_count/vpacked_forms_count/vaffixes_size/vform_no/vpos_id';
//      return 'Voffset/vcplen/vplen/vflen/vcommon_ancode/vforms_count/vpacked_forms_count/vaffixes_size/vpos_id';
    }

    protected function getUnpackBlockSize() {
        return 22;
    }
}

class phpMorphy_AnnotDecoder_Predict extends phpMorphy_AnnotDecoder_Common {
    protected function getUnpackString() {
//      return 'Voffset/vcplen/vplen/vflen/vcommon_ancode/vforms_count/vpacked_forms_count/vaffixes_size/vform_no/vpos_id/vfreq';
        return parent::getUnpackString() . '/vfreq';
    }

    protected function getUnpackBlockSize() {
        return parent::getUnpackBlockSize() + 2;
    }
}

class phpMorphy_AnnotDecoder_Factory {
    protected static $instances = array();

    protected
        $cache_common,
        $cache_predict,
        $eos;

    protected function __construct($eos) {
        $this->eos = $eos;
    }

    static function create($eos) {
        if(!isset(self::$instances[$eos])) {
            self::$instances[$eos] = new phpMorphy_AnnotDecoder_Factory($eos);
        }

        return self::$instances[$eos];
    }

    function getCommonDecoder() {
        if(!isset($this->cache_common)) {
            $this->cache_common = $this->instantinate('common');
        }

        return $this->cache_common;
    }

    function getPredictDecoder() {
        if(!isset($this->cache_predict)) {
            $this->cache_predict = $this->instantinate('predict');
        }

        return $this->cache_predict;
    }

    protected function instantinate($type) {
        $clazz = 'phpMorphy_AnnotDecoder_' . ucfirst($GLOBALS['__phpmorphy_strtolower']($type));

        return new $clazz($this->eos);
    }
}

interface phpMorphy_AncodesResolver_Interface {
    function resolve($ancodeId);
    function unresolve($ancode);
}

class phpMorphy_AncodesResolver_Proxy implements phpMorphy_AncodesResolver_Interface {
    protected
        $args, $class;
        //$__obj;

    function __construct($class, $ctorArgs) {
        $this->class = $class;
        $this->args = $ctorArgs;
    }

    function unresolve($ancode) {
        return $this->__obj->unresolve($ancode);
    }

    function resolve($ancodeId) {
        return $this->__obj->resolve($ancodeId);
    }

    static function instantinate($class, $args) {
        $ref = new ReflectionClass($class);
        return $ref->newInstanceArgs($args);
    }

    function __get($propName) {
        if($propName === '__obj') {
            $this->__obj = $this->instantinate($this->class, $this->args);

            unset($this->args);
            unset($this->class);

            return $this->__obj;
        }

        throw new phpMorphy_Exception("Unknown '$propName' property");
    }
}

class phpMorphy_AncodesResolver_ToText implements phpMorphy_AncodesResolver_Interface {
    protected $gramtab;

    function __construct(phpMorphy_GramTab_Interface $gramtab) {
        $this->gramtab = $gramtab;
    }

    function resolve($ancodeId) {
        if(!isset($ancodeId)) {
            return null;
        }

        return $this->gramtab->ancodeToString($ancodeId);
    }

    function unresolve($ancode) {
        return $this->gramtab->stringToAncode($ancode);
        //throw new phpMorphy_Exception("Can`t convert grammar info in text into ancode id");
    }
}

class phpMorphy_AncodesResolver_ToDialingAncodes implements phpMorphy_AncodesResolver_Interface {
    protected
        $ancodes_map,
        $reverse_map;

    function __construct(phpMorphy_Storage $ancodesMap) {
        if(false === ($this->ancodes_map = unserialize($ancodesMap->read(0, $ancodesMap->getFileSize())))) {
            throw new phpMorphy_Exception("Can`t open phpMorphy => Dialing ancodes map");
        }

        $this->reverse_map = array_flip($this->ancodes_map);
    }

    function unresolve($ancode) {
        if(!isset($ancode)) {
            return null;
        }

        if(!isset($this->reverse_map[$ancode])) {
            throw new phpMorphy_Exception("Unknwon ancode found '$ancode'");
        }

        return $this->reverse_map[$ancode];
    }

    function resolve($ancodeId) {
        if(!isset($ancodeId)) {
            return null;
        }

        if(!isset($this->ancodes_map[$ancodeId])) {
            throw new phpMorphy_Exception("Unknwon ancode id found '$ancodeId'");
        }

        return $this->ancodes_map[$ancodeId];
    }
}

class phpMorphy_AncodesResolver_AsIs implements phpMorphy_AncodesResolver_Interface {
    // This ctor for ReflectionClass::newInstanceArgs($args) with $args = array()
    function __construct() {
    }

    function resolve($ancodeId) {
        return $ancodeId;
    }

    function unresolve($ancode) {
        return $ancode;
    }
}

// ----------------------------
// Helper
// ----------------------------
class phpMorphy_Morphier_Helper {
    protected
        $graminfo,
        $annot_decoder,
        $char_size,
        $ends,
        $gramtab,
        $ancodes_resolver,
        $gramtab_consts_included = false,
        $resolve_pos;

    function __construct(
        phpMorphy_GramInfo_Interace $graminfo,
        phpMorphy_GramTab_Interface $gramtab,
        phpMorphy_AncodesResolver_Interface $ancodesResolver,
        $resolvePartOfSpeech
    ) {
        $this->graminfo = $graminfo;
        $this->gramtab = $gramtab;
        $this->resolve_pos = (bool)$resolvePartOfSpeech;
        $this->ancodes_resolver = $ancodesResolver;

        $this->char_size = $graminfo->getCharSize();

        $this->ends = $graminfo->getEnds();
    }

    function setAnnotDecoder(phpMorphy_AnnotDecoder_Interface $annotDecoder) {
        $this->annot_decoder = $annotDecoder;
    }

    // getters
    function getEndOfString() {
        return $this->ends;
    }

    function getCharSize() {
        return $this->char_size;
    }

    function hasAnnotDecoder() {
        return isset($this->annot_decoder);
    }

    function getAnnotDecoder() {
        return $this->annot_decoder;
    }

    function getAncodesResolver() {
        return $this->ancodes_resolver;
    }

    function getGramInfo() {
        return $this->graminfo;
    }

    function getGramTab() {
        return $this->gramtab;
    }

    function isResolvePartOfSpeech() {
        return $this->resolve_pos;
    }

    // other
    function resolvePartOfSpeech($posId) {
        return $this->gramtab->resolvePartOfSpeechId($posId);
    }

    function getGrammems($ancodeId) {
        return $this->gramtab->getGrammems($ancodeId);
    }

    function getGrammemsAndPartOfSpeech($ancodeId) {
        return array(
            $this->gramtab->getPartOfSpeech($ancodeId),
            $this->gramtab->getGrammems($ancodeId)
        );
    }

    function extractPartOfSpeech($annot) {
        if($this->resolve_pos) {
            return $this->resolvePartOfSpeech($annot['pos_id']);
        } else {
            return $annot['pos_id'];
        }
    }

    protected function includeGramTabConsts() {
        if($this->isResolvePartOfSpeech()) {
            $this->gramtab->includeConsts();
        }

        $this->gramtab_consts_included = true;
    }

    // getters
    function getWordDescriptor($word, $annots) {
        if(!$this->gramtab_consts_included) {
            $this->includeGramTabConsts();
        }

        return new phpMorphy_WordDescriptor_Collection($word, $annots, $this);
    }

    protected function getBaseAndPrefix($word, $cplen, $plen, $flen) {
        if($flen) {
            $base = $GLOBALS['__phpmorphy_substr']($word, $cplen + $plen, -$flen);
        } else {
            if($cplen || $plen) {
                $base = $GLOBALS['__phpmorphy_substr']($word, $cplen + $plen);
            } else {
                $base = $word;
            }
        }

        $prefix = $cplen ? $GLOBALS['__phpmorphy_substr']($word, 0, $cplen) : '';

        return array($base, $prefix);
    }

    function getPartOfSpeech($word, $annots) {
        if(false === $annots) {
            return false;
        }

        $result = array();

        foreach($this->decodeAnnot($annots, false) as $annot) {
            $result[$this->extractPartOfSpeech($annot)] = 1;
        }

        return array_keys($result);
    }

    function getBaseForm($word, $annots) {
        if(false === $annots) {
            return false;
        }

        $annots = $this->decodeAnnot($annots, true);

        return $this->composeBaseForms($word, $annots);
    }

    function getPseudoRoot($word, $annots) {
        if(false === $annots) {
            return false;
        }

        $annots = $this->decodeAnnot($annots, false);

        $result = array();

        foreach($annots as $annot) {
            list($base) = $this->getBaseAndPrefix(
                $word,
                $annot['cplen'],
                $annot['plen'],
                $annot['flen']
            );

            $result[$base] = 1;
        }

        return array_keys($result);
    }

    function getAllForms($word, $annots) {
        if(false === $annots) {
            return false;
        }

        $annots = $this->decodeAnnot($annots, false);

        return $this->composeForms($word, $annots);
    }

    function castFormByGramInfo($word, $annots, $partOfSpeech, $grammems, $returnWords = false, $callback = null) {
        if(false === $annots) {
            return false;
        }

        if(isset($callback) && !is_callable($callback)) {
            throw new phpMorphy_Exception("Invalid callback given");
        }

        $result = array();
        $grammems = (array)$grammems;
        $partOfSpeech = isset($partOfSpeech) ? (string)$partOfSpeech : null;

        foreach($this->decodeAnnot($annots, false) as $annot) {
            $all_ancodes = $this->graminfo->readAncodes($annot);
            $flexias = $this->graminfo->readFlexiaData($annot);
            $common_ancode = $annot['common_ancode'];
            $common_grammems = isset($common_ancode) ? $this->gramtab->getGrammems($common_ancode) : array();

            list($base, $prefix) = $this->getBaseAndPrefix(
                $word,
                $annot['cplen'],
                $annot['plen'],
                $annot['flen']
            );

            // i use strange $form_no handling for perfomance issue (no function call overhead)
            $i = 0;
            $form_no = 0;
            foreach($all_ancodes as $form_ancodes) {
                foreach($form_ancodes as $ancode) {
                    $form_pos = $this->gramtab->getPartOfSpeech($ancode);
                    $form_grammems = array_merge($this->gramtab->getGrammems($ancode), $common_grammems);
                    $form = $prefix . $flexias[$i] . $base . $flexias[$i + 1];

                    if(isset($callback)) {
                        if(!call_user_func($callback, $form, $form_pos, $form_grammems, $form_no)) {
                            $form_no++;
                            continue;
                        }
                    } else {
                        if(isset($partOfSpeech) && $form_pos !== $partOfSpeech) {
                            $form_no++;
                            continue;
                        }

                        if(count(array_diff($grammems, $form_grammems)) > 0) {
                            $form_no++;
                            continue;
                        }
                    }

                    if($returnWords) {
                        $result[$form] = 1;
                    } else {
                        $result[] = array(
                            'form' => $form,
                            'form_no' => $form_no,
                            'pos' => $form_pos,
                            'grammems' => $form_grammems
                        );
                    }

                    $form_no++;
                }

                $i += 2;
            }
        }

        return $returnWords ? array_keys($result) : $result;
    }

    function getAncode($annots) {
        if(false === $annots) {
            return false;
        }

        $result = array();

        foreach($this->decodeAnnot($annots, false) as $annot) {
            $all_ancodes = $this->graminfo->readAncodes($annot);

            $result[] = array(
                'common' => $this->ancodes_resolver->resolve($annot['common_ancode']),
                'all' => array_map(
                    array($this->ancodes_resolver, 'resolve'),
                    $all_ancodes[$annot['form_no']]
                )
            );
        }

        return $this->array_unique($result);
    }

    protected static function array_unique($array) {
        static $need_own;

        if(!isset($need_own)) {
            $need_own = -1 === version_compare(PHP_VERSION, '5.2.9');
        }

        if($need_own) {
            $result = array();

            foreach(array_keys(array_unique(array_map('serialize', $array))) as $key) {
                $result[$key] = $array[$key];
            }

            return $result;
        } else {
            return array_unique($array, SORT_REGULAR);
        }
    }

    function getGrammarInfoMergeForms($annots) {
        if(false === $annots) {
            return false;
        }

        $result = array();

        foreach($this->decodeAnnot($annots, false) as $annot) {
            $all_ancodes = $this->graminfo->readAncodes($annot);
            $common_ancode = $annot['common_ancode'];
            $grammems = isset($common_ancode) ? $this->gramtab->getGrammems($common_ancode) : array();

            $forms_count = 0;
            $form_no = $annot['form_no'];

            foreach($all_ancodes[$form_no] as $ancode) {
                $grammems = array_merge($grammems, $this->gramtab->getGrammems($ancode));
                $forms_count++;
            }

            $grammems = array_unique($grammems);
            sort($grammems);

            $result[] = array(
                // part of speech identical across all joined forms
                'pos' => $this->gramtab->getPartOfSpeech($ancode),
                'grammems' => $grammems,
                'forms_count' => $forms_count,
                'form_no_low' => $form_no,
                'form_no_high' => $form_no + $forms_count,
            );
        }

        return $this->array_unique($result);
    }

    function getGrammarInfo($annots) {
        if(false === $annots) {
            return false;
        }

        $result = array();

        foreach($this->decodeAnnot($annots, false) as $annot) {
            $all_ancodes = $this->graminfo->readAncodes($annot);
            $common_ancode = $annot['common_ancode'];
            $common_grammems = isset($common_ancode) ? $this->gramtab->getGrammems($common_ancode) : array();

            $info = array();

            $form_no = $annot['form_no'];
            foreach($all_ancodes[$form_no] as $ancode) {
                $grammems = //array_unique(
                    array_merge($common_grammems, $this->gramtab->getGrammems($ancode));
                //);

                sort($grammems);

                $info_item = array(
                    'pos' => $this->gramtab->getPartOfSpeech($ancode),
                    'grammems' => $grammems,
                    'form_no' => $form_no,
                );

                $info[] = $info_item;
            }

            $unique_info = $this->array_unique($info);
            sort($unique_info);
            $result[] = $unique_info;
        }

        return $this->array_unique($result);
    }

    function getAllFormsWithResolvedAncodes($word, $annots, $resolveType = 'no_resolve') {
        if(false === $annots) {
            return false;
        }

        $annots = $this->decodeAnnot($annots, false);

        return $this->composeFormsWithResolvedAncodes($word, $annots);
    }

    function getAllFormsWithAncodes($word, $annots, &$foundFormNo = array()) {
        if(false === $annots) {
            return false;
        }

        $annots = $this->decodeAnnot($annots, false);

        return $this->composeFormsWithAncodes($word, $annots, $foundFormNo);
    }

    function getAllAncodes($word, $annots) {
        if(false === $annots) {
            return false;
        }

        $result = array();

        foreach($annots as $annot) {
            $result[] = $this->graminfo->readAncodes($annot);
        }

        return $result;
    }

    protected function composeBaseForms($word, $annots) {
        $result = array();

        foreach($annots as $annot) {

            if($annot['form_no'] > 0) {
                list($base, $prefix) = $this->getBaseAndPrefix(
                    $word,
                    $annot['cplen'],
                    $annot['plen'],
                    $annot['flen']
                );

                $result[$prefix . $annot['base_prefix'] . $base . $annot['base_suffix']] = 1;
            } else {
                $result[$word] = 1;
            }
        }

        return array_keys($result);
    }

    protected function composeForms($word, $annots) {
        $result = array();

        foreach($annots as $annot) {
            list($base, $prefix) = $this->getBaseAndPrefix(
                $word,
                $annot['cplen'],
                $annot['plen'],
                $annot['flen']
            );

            // read flexia
            $flexias = $this->graminfo->readFlexiaData($annot);

            for($i = 0, $c = count($flexias); $i < $c; $i += 2) {
                $result[$prefix . $flexias[$i] . $base . $flexias[$i + 1]] = 1;
            }
        }

        return array_keys($result);
    }

    protected function composeFormsWithResolvedAncodes($word, $annots) {
        $result = array();

        foreach($annots as $annotIdx => $annot) {
            list($base, $prefix) = $this->getBaseAndPrefix(
                $word,
                $annot['cplen'],
                $annot['plen'],
                $annot['flen']
            );

            $words = array();
            $ancodes = array();
            $common_ancode = $annot['common_ancode'];

            // read flexia
            $flexias = $this->graminfo->readFlexiaData($annot);
            $all_ancodes = $this->graminfo->readAncodes($annot);

            for($i = 0, $c = count($flexias); $i < $c; $i += 2) {
                $form = $prefix . $flexias[$i] . $base . $flexias[$i + 1];

                $current_ancodes = $all_ancodes[$i / 2];
                foreach($current_ancodes as $ancode) {
                    $words[] = $form;
                    $ancodes[] = $this->ancodes_resolver->resolve($ancode);
                }
            }

            $result[] = array(
                'forms' => $words,
                'common' => $this->ancodes_resolver->resolve($common_ancode),
                'all' => $ancodes,
            );
        }

        return $result;
    }

    protected function composeFormsWithAncodes($word, $annots, &$foundFormNo) {
        $result = array();

        foreach($annots as $annotIdx => $annot) {
            list($base, $prefix) = $this->getBaseAndPrefix(
                $word,
                $annot['cplen'],
                $annot['plen'],
                $annot['flen']
            );

            // read flexia
            $flexias = $this->graminfo->readFlexiaData($annot);
            $ancodes = $this->graminfo->readAncodes($annot);

            $found_form_no = $annot['form_no'];

            $foundFormNo = !is_array($foundFormNo) ? array() : $foundFormNo;

            for($i = 0, $c = count($flexias); $i < $c; $i += 2) {
                $form_no = $i / 2;
                $word = $prefix . $flexias[$i] . $base . $flexias[$i + 1];

                if($found_form_no == $form_no) {
                    $count = count($result);
                    $foundFormNo[$annotIdx]['low'] = $count;
                    $foundFormNo[$annotIdx]['high'] = $count + count($ancodes[$form_no]) - 1;
                }

                foreach($ancodes[$form_no] as $ancode) {
                    $result[] = array($word, $ancode);
                }
            }
        }

        return $result;
    }

    function decodeAnnot($annotsRaw, $withBase) {
        if(is_array($annotsRaw)) {
            return $annotsRaw;
        } else {
            return $this->annot_decoder->decode($annotsRaw, $withBase);
        }
    }
}

// ----------------------------
// WordDescriptor
// ----------------------------
// TODO: extend ArrayObject?
class phpMorphy_WordDescriptor_Collection implements Countable, IteratorAggregate, ArrayAccess {
    protected
        $word,
        $descriptors = array(),
        $helper;

    function __construct($word, $annots, phpMorphy_Morphier_Helper $helper) {
        $this->word = (string)$word;
        $this->annots = false === $annots ? false : $helper->decodeAnnot($annots, true);

        $this->helper = $helper;

        if(false !== $this->annots) {
            foreach($this->annots as $annot) {
                $this->descriptors[] = $this->createDescriptor($word, $annot, $helper);
            }
        }
    }

    protected function createDescriptor($word, $annot, phpMorphy_Morphier_Helper $helper) {
        return new phpMorphy_WordDescriptor($word, $annot, $helper);
    }

    function getDescriptor($index) {
        if(!$this->offsetExists($index)) {
            throw new phpMorphy_Exception("Invalid index '$index' specified");
        }

        return $this->descriptors[$index];
    }

    function getByPartOfSpeech($poses) {
        $result = array();
        settype($poses, 'array');

        foreach($this as $desc) {
            if($desc->hasPartOfSpeech($poses)) {
                $result[] = $desc;
            }
        }

//        return count($result) ? $result : false;
        return $result;
    }

    function offsetExists($off) {
        return isset($this->descriptors[$off]);
    }

    function offsetUnset($off) {
        throw new phpMorphy_Exception(__CLASS__ . " is not mutable");
    }

    function offsetSet($off, $value) {
        throw new phpMorphy_Exception(__CLASS__ . " is not mutable");
    }

    function offsetGet($off) {
        return $this->getDescriptor($off);
    }

    function count() {
        return count($this->descriptors);
    }

    function getIterator() {
        return new ArrayIterator($this->descriptors);
    }
}

class phpMorphy_WordForm {
    protected
        $word,
        $form_no,
        $pos_id,
        $grammems
        ;

    function __construct($word, $form_no, $pos_id, $grammems) {
        $this->word = (string)$word;
        $this->form_no = (int)$form_no;
        $this->pos_id = $pos_id;

        sort($grammems);
        $this->grammems = $grammems;
    }

    function getPartOfSpeech() {
        return $this->pos_id;
    }

    function getGrammems() {
        return $this->grammems;
    }

    function hasGrammems($grammems) {
        $grammems = (array)$grammems;

        $grammes_count = count($grammems);
        return $grammes_count && count(array_intersect($grammems, $this->grammems)) == $grammes_count;
    }

    static function compareGrammems($a, $b) {
        return count($a) == count($b) && count(array_diff($a, $b)) == 0;
    }

    function getWord() {
        return $this->word;
    }

    function getFormNo() {
        return $this->form_no;
    }
}

class phpMorphy_WordDescriptor implements Countable, ArrayAccess, IteratorAggregate {
    protected
        $word,
        $annot,
        $helper,
        $cached_forms,
        $cached_base,
        $cached_pseudo_root,
        $all_forms,
        $found_form_no,
        $common_ancode_grammems;

    function __construct($word, $annot, phpMorphy_Morphier_Helper $helper) {
        $this->word = (string)$word;
        $this->annot = array($annot);

        $this->helper = $helper;
    }

    function getPseudoRoot() {
        if(!isset($this->cached_pseudo_root)) {
            list($this->cached_pseudo_root) = $this->helper->getPseudoRoot($this->word, $this->annot);
        }

        return $this->cached_pseudo_root;
    }

    function getBaseForm() {
        if(!isset($this->cached_base)) {
            list($this->cached_base) = $this->helper->getBaseForm($this->word, $this->annot);
        }

        return $this->cached_base;
    }

    function getAllForms() {
        if(!isset($this->cached_forms)) {
            $this->cached_forms = $this->helper->getAllForms($this->word, $this->annot);
        }

        return $this->cached_forms;
    }

    function getWordForm($index) {
        $this->readAllForms();

        if(!$this->offsetExists($index)) {
            throw new phpMorphy_Exception("Invalid index '$index' given");
        }

        return $this->all_forms[$index];
    }

    protected function createWordForm($word, $form_no, $ancode) {
        if(!isset($this->common_ancode_grammems)) {
            $common_ancode = $this->annot[0]['common_ancode'];

            $this->common_ancode_grammems = isset($common_ancode) ?
                $this->helper->getGrammems($common_ancode) :
                array();
        }

        list($pos_id, $all_grammems) = $this->helper->getGrammemsAndPartOfSpeech($ancode);

        return new phpMorphy_WordForm($word, $form_no, $pos_id, array_merge($this->common_ancode_grammems, $all_grammems));
    }

    protected function readAllForms() {
        if(!isset($this->all_forms)) {
            $result = array();

            $form_no = 0;

            $found_form_no = array();
            foreach($this->helper->getAllFormsWithAncodes($this->word, $this->annot, $found_form_no) as $form) {
                $word = $form[0];

                $result[] = $this->createWordForm($word, $form_no, $form[1]);

                $form_no++;
            }

            $this->found_form_no = $found_form_no[0];
            $this->all_forms = $result;
        }

        return $this->all_forms;
    }

    protected function getFoundFormNoLow() {
        $this->readAllForms();

        return $this->found_form_no['low'];
    }

    protected function getFoundFormNoHigh() {
        $this->readAllForms();

        return $this->found_form_no['high'];
    }

    function getFoundWordForm() {
        $result = array();
        for($i = $this->getFoundFormNoLow(), $c = $this->getFoundFormNoHigh() + 1; $i < $c; $i++) {
            $result[] = $this->getWordForm($i);
        }

        return $result;
    }

    function hasGrammems($grammems) {
        settype($grammems, 'array');

        foreach($this as $wf) {
            if($wf->hasGrammems($grammems)) {
                return true;
            }
        }

        return false;
    }

    function getWordFormsByGrammems($grammems) {
        settype($grammems, 'array');
        $result = array();

        foreach($this as $wf) {
            if($wf->hasGrammems($grammems)) {
                $result[] = $wf;
            }
        }

        return $result;
//        return count($result) ? $result : false;
    }

    function hasPartOfSpeech($poses) {
        settype($poses, 'array');

        foreach($this as $wf) {
            if(in_array($wf->getPartOfSpeech(), $poses, true)) {
                return true;
            }
        }

        return false;
    }

    function getWordFormsByPartOfSpeech($poses) {
        settype($poses, 'array');
        $result = array();

        foreach($this as $wf) {
            if(in_array($wf->getPartOfSpeech(), $poses, true)) {
                $result[] = $wf;
            }
        }

        return $result;
//        return count($result) ? $result : false;
    }

    function count() {
        return count($this->readAllForms());
    }

    function offsetExists($off) {
        $this->readAllForms();

        return isset($this->all_forms[$off]);
    }

    function offsetSet($off, $value) {
        throw new phpMorphy_Exception(__CLASS__ . " is not mutable");
    }

    function offsetUnset($off) {
        throw new phpMorphy_Exception(__CLASS__ . " is not mutable");
    }

    function offsetGet($off) {
        return $this->getWordForm($off);
    }

    function getIterator() {
        $this->readAllForms();

        return new ArrayIterator($this->all_forms);
    }
}

// ----------------------------
// Finders
// ----------------------------
interface phpMorphy_Morphier_Finder_Interface {
    function findWord($word);
    function decodeAnnot($raw, $withBase);
    function getAnnotDecoder();
}

abstract class phpMorphy_Morphier_Finder_Base implements phpMorphy_Morphier_Finder_Interface {
    protected
        $annot_decoder,
        $prev_word,
        $prev_result = false;

    function __construct(phpMorphy_AnnotDecoder_Interface $annotDecoder) {
        $this->annot_decoder = $annotDecoder;
    }

    function findWord($word) {
        if($this->prev_word === $word) {
            return $this->prev_result;
        }

        $result = $this->doFindWord($word);

        $this->prev_word = $word;
        $this->prev_result = $result;

        return $result;
    }

    function getAnnotDecoder() {
        return $this->annot_decoder;
    }

    function decodeAnnot($raw, $withBase) {
        return $this->annot_decoder->decode($raw, $withBase);
    }

    abstract protected function doFindWord($word);
}

class phpMorphy_Morphier_Finder_Common extends phpMorphy_Morphier_Finder_Base {
    protected
        $fsa,
        $root;

    function __construct(phpMorphy_Fsa_Interface $fsa, phpMorphy_AnnotDecoder_Interface $annotDecoder) {
        parent::__construct($annotDecoder);

        $this->fsa = $fsa;
        $this->root = $this->fsa->getRootTrans();
    }

    function getFsa() {
        return $this->fsa;
    }

    protected function doFindWord($word) {
        $result = $this->fsa->walk($this->root, $word);

        if(!$result['result'] || null === $result['annot']) {
            return false;
        }

        return $result['annot'];
    }
}

class phpMorphy_Morphier_Finder_Predict_Suffix extends phpMorphy_Morphier_Finder_Common {
    protected
        $min_suf_len,
        $unicode;

    function __construct(phpMorphy_Fsa_Interface $fsa, phpMorphy_AnnotDecoder_Interface $annotDecoder, $encoding, $minimalSuffixLength = 4) {
        parent::__construct($fsa, $annotDecoder);

        $this->min_suf_len = (int)$minimalSuffixLength;
        $this->unicode = phpMorphy_UnicodeHelper::create($encoding);
    }

    protected function doFindWord($word) {
        $word_len = $this->unicode->strlen($word);

        if(!$word_len) {
            return false;
        }

        for($i = 1, $c = $word_len - $this->min_suf_len; $i < $c; $i++) {
            $word = $GLOBALS['__phpmorphy_substr']($word, $this->unicode->firstCharSize($word));

            if(false !== ($result = parent::doFindWord($word))) {
                break;
            }
        }

        if($i < $c) {
            //$known_len = $word_len - $i;
            $unknown_len = $i;

            return $result;
            /*
            return $this->fixAnnots(
                $this->decodeAnnot($result, true),
                $unknown_len
            );
            */
        } else {
            return false;
        }
    }

    protected function fixAnnots($annots, $len) {
        for($i = 0, $c = count($annots); $i < $c; $i++) {
            $annots[$i]['cplen'] = $len;
        }

        return $annots;
    }
}

class phpMorphy_Morphier_PredictCollector extends phpMorphy_Fsa_WordsCollector {
    protected
        $used_poses = array(),
        $annot_decoder,
        $collected = 0;

    function __construct($limit, phpMorphy_AnnotDecoder_Interface $annotDecoder) {
        parent::__construct($limit);

        $this->annot_decoder = $annotDecoder;
    }

    function collect($path, $annotRaw) {
        if($this->collected > $this->limit) {
            return false;
        }

        $used_poses =& $this->used_poses;
        $annots = $this->decodeAnnot($annotRaw);

        for($i = 0, $c = count($annots); $i < $c; $i++) {
            $annot = $annots[$i];
            $annot['cplen'] = $annot['plen'] = 0;

            $pos_id = $annot['pos_id'];

            if(isset($used_poses[$pos_id])) {
                $result_idx = $used_poses[$pos_id];

                if($annot['freq'] > $this->items[$result_idx]['freq']) {
                    $this->items[$result_idx] = $annot;
                }
            } else {
                $used_poses[$pos_id] = count($this->items);
                $this->items[] = $annot;
            }
        }

        $this->collected++;
        return true;
    }

    function clear() {
        parent::clear();
        $this->collected = 0;
        $this->used_poses = array();
    }

    function decodeAnnot($annotRaw) {
        return $this->annot_decoder->decode($annotRaw, true);
    }
}

class phpMorphy_Morphier_Finder_Predict_Databse extends phpMorphy_Morphier_Finder_Common {
    protected
        $collector,
        $unicode,
        $graminfo,
        $min_postfix_match;

    function __construct(
        phpMorphy_Fsa_Interface $fsa,
        phpMorphy_AnnotDecoder_Interface $annotDecoder,
        $encoding,
        phpMorphy_GramInfo_Interace $graminfo,
        $minPostfixMatch = 2,
        $collectLimit = 32
    ) {
        parent::__construct($fsa, $annotDecoder);

        $this->graminfo = $graminfo;
        $this->min_postfix_match = $minPostfixMatch;
        $this->collector = $this->createCollector($collectLimit, $this->getAnnotDecoder());

        $this->unicode = phpMorphy_UnicodeHelper::create($encoding);
    }

    protected function createAnnotDecoder() {
        return phpmorphy_annot_decoder_new('predict');
    }

    protected function doFindWord($word) {
        $rev_word = $this->unicode->strrev($word);
        $result = $this->fsa->walk($this->root, $rev_word);

        if($result['result'] && null !== $result['annot']) {
            $annots = $result['annot'];
        } else {
            $match_len = $this->unicode->strlen($this->unicode->fixTrailing($GLOBALS['__phpmorphy_substr']($rev_word, 0, $result['walked'])));

            if(null === ($annots = $this->determineAnnots($result['last_trans'], $match_len))) {
                return false;
            }
        }

        if(!is_array($annots)) {
            $annots = $this->collector->decodeAnnot($annots);
        }

        return $this->fixAnnots($word, $annots);
    }

    protected function determineAnnots($trans, $matchLen) {
        $annots = $this->fsa->getAnnot($trans);

        if(null == $annots && $matchLen >= $this->min_postfix_match) {
            $this->collector->clear();

            $this->fsa->collect(
                $trans,
                $this->collector->getCallback()
            );

            $annots = $this->collector->getItems();
        }

        return $annots;
    }

    protected function fixAnnots($word, $annots) {
        $result = array();

        // remove all prefixes?
        for($i = 0, $c = count($annots); $i < $c; $i++) {
            $annot = $annots[$i];

            $annot['cplen'] = $annot['plen'] = 0;

            $flexias = $this->graminfo->readFlexiaData($annot, false);

            $prefix = $flexias[$annot['form_no'] * 2];
            $suffix = $flexias[$annot['form_no'] * 2 + 1];

            $plen = $GLOBALS['__phpmorphy_strlen']($prefix);
            $slen = $GLOBALS['__phpmorphy_strlen']($suffix);
            if(
                (!$plen || $GLOBALS['__phpmorphy_substr']($word, 0, $GLOBALS['__phpmorphy_strlen']($prefix)) === $prefix) &&
                (!$slen || $GLOBALS['__phpmorphy_substr']($word, -$GLOBALS['__phpmorphy_strlen']($suffix)) === $suffix)
            ) {
                $result[] = $annot;
            }
        }

        return count($result) ? $result : false;
    }

    protected function createCollector($limit) {
        return new phpMorphy_Morphier_PredictCollector($limit, $this->getAnnotDecoder());
    }
}

// ----------------------------
// Morphiers
// ----------------------------
abstract class phpMorphy_Morphier_Base implements phpMorphy_Morphier_Interface {
    protected
        /**
         * @var phpMorphy_Morphier_Finder_Interface
         */
        $finder,
        /**
         * @var phpMorphy_Morphier_Helper
         */
        $helper;

    function __construct(phpMorphy_Morphier_Finder_Interface $finder, phpMorphy_Morphier_Helper $helper) {
        $this->finder = $finder;

        $this->helper = clone $helper;
        $this->helper->setAnnotDecoder($finder->getAnnotDecoder());
    }

    /**
     * @return phpMorphy_Morphier_Finder_Interface
     */
    function getFinder() {
        return $this->finder;
    }

    /**
     * @return phpMorphy_Morphier_Helper
     */
    function getHelper() {
        return $this->helper;
    }

    function getAnnot($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->decodeAnnot($annots, true);
    }

    function getWordDescriptor($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->getWordDescriptor($word, $annots);
    }

    function getAllFormsWithAncodes($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->getAllFormsWithResolvedAncodes($word, $annots);
    }

    function getPartOfSpeech($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->getPartOfSpeech($word, $annots);
    }

    function getBaseForm($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->getBaseForm($word, $annots);
    }

    function getPseudoRoot($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->getPseudoRoot($word, $annots);
    }

    function getAllForms($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->getAllForms($word, $annots);
    }

    function getAncode($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->getAncode($annots);
    }

    function getGrammarInfo($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->getGrammarInfo($annots);
    }

    function getGrammarInfoMergeForms($word) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->getGrammarInfoMergeForms($annots);
    }

    function castFormByGramInfo($word, $partOfSpeech, $grammems, $returnOnlyWord = false, $callback = null) {
        if(false === ($annots = $this->finder->findWord($word))) {
            return false;
        }

        return $this->helper->castFormByGramInfo($word, $annots);
    }

    function castFormByPattern($word, $patternWord, $returnOnlyWord = false, $callback = null) {
        if(false === ($orig_annots = $this->finder->findWord($word))) {
            return false;
        }

        if(false === ($pattern_annots = $this->finder->findWord($patternWord))) {
            return false;
        }

        return $this->helper->castFormByPattern(
            $word, $orig_annots,
            $patternWord, $pattern_annots,
            $returnOnlyWord,
            $callback
        );
    }
};

class phpMorphy_Morphier_Common extends phpMorphy_Morphier_Base {
    function __construct(phpMorphy_Fsa_Interface $fsa, phpMorphy_Morphier_Helper $helper) {
        parent::__construct(
            new phpMorphy_Morphier_Finder_Common(
                $fsa,
                $this->createAnnotDecoder($helper)
            ),
            $helper
        );
    }

    protected function createAnnotDecoder(phpMorphy_Morphier_Helper $helper) {
        return phpMorphy_AnnotDecoder_Factory::create($helper->getGramInfo()->getEnds())->getCommonDecoder();
    }
};

class phpMorphy_Morphier_Predict_Suffix extends phpMorphy_Morphier_Base {
    function __construct(phpMorphy_Fsa_Interface $fsa, phpMorphy_Morphier_Helper $helper) {
        parent::__construct(
            new phpMorphy_Morphier_Finder_Predict_Suffix(
                $fsa,
                $this->createAnnotDecoder($helper),
                $helper->getGramInfo()->getEncoding(),
                4
            ),
            $helper
        );
    }

    protected function createAnnotDecoder(phpMorphy_Morphier_Helper $helper) {
        return phpMorphy_AnnotDecoder_Factory::create($helper->getGramInfo()->getEnds())->getCommonDecoder();
    }
}

class phpMorphy_Morphier_Predict_Database extends phpMorphy_Morphier_Base {
    function __construct(phpMorphy_Fsa_Interface $fsa, phpMorphy_Morphier_Helper $helper) {
        parent::__construct(
            new phpMorphy_Morphier_Finder_Predict_Databse(
                $fsa,
                $this->createAnnotDecoder($helper),
                $helper->getGramInfo()->getEncoding(),
                $helper->getGramInfo(),
                2,
                32
            ),
            $helper
        );
    }

    protected function createAnnotDecoder(phpMorphy_Morphier_Helper $helper) {
        return phpMorphy_AnnotDecoder_Factory::create($helper->getGramInfo()->getEnds())->getPredictDecoder();
    }
}

class phpMorphy_Morphier_Bulk implements phpMorphy_Morphier_Interface {
    protected
        $fsa,
        $root_trans,
        $helper,
        $notfound = array(),
        $graminfo;

    function __construct(phpMorphy_Fsa_Interface $fsa, phpMorphy_Morphier_Helper $helper) {
        $this->fsa = $fsa;
        $this->root_trans = $fsa->getRootTrans();

        $this->helper = clone $helper;
        $this->helper->setAnnotDecoder($this->createAnnotDecoder($helper));

        $this->graminfo = $helper->getGramInfo();
    }

    function getFsa() {
        return $this->fsa;
    }

    function getHelper() {
        return $this->helper;
    }

    function getGraminfo() {
        return $this->graminfo;
    }

    function getNotFoundWords() {
        return $this->notfound;
    }

    protected function createAnnotDecoder(phpMorphy_Morphier_Helper $helper) {
        return new phpMorphy_AnnotDecoder_Common($helper->getGramInfo()->getEnds());
    }

    function getAnnot($word) {
        $result = array();

        foreach($this->findWord($word) as $annot => $words) {
            $annot = $this->helper->decodeAnnot($annot, true);

            foreach($words as $word) {
                $result[$word][] = $annot;
            }
        }

        return $result;
    }

    function getBaseForm($words) {
        $annots = $this->findWord($words);

        return $this->composeForms($annots, true, false, false);
    }

    function getAllForms($words) {
        $annots = $this->findWord($words);

        return $this->composeForms($annots, false, false, false);
    }

    function getPseudoRoot($words) {
        $annots = $this->findWord($words);

        return $this->composeForms($annots, false, true, false);
    }

    function getPartOfSpeech($words) {
        $annots = $this->findWord($words);

        return $this->composeForms($annots, false, false, true);
    }

    protected function processAnnotsWithHelper($words, $method, $callWithWord = false) {
        $result = array();

        foreach($this->findWord($words) as $annot_raw => $words) {
            if($GLOBALS['__phpmorphy_strlen']($annot_raw) == 0) continue;

            if($callWithWord) {
                foreach($words as $word) {
                    $result[$word] = $this->helper->$method($word, $annot_raw);
                }
            } else {
                $result_for_annot = $this->helper->$method($annot_raw);

                foreach($words as $word) {
                    $result[$word] = $result_for_annot;
                }
            }
        }

        return $result;
    }

    function getAncode($words) {
        return $this->processAnnotsWithHelper($words, 'getAncode');
    }

    function getGrammarInfoMergeForms($words) {
        return $this->processAnnotsWithHelper($words, 'getGrammarInfoMergeForms');
    }

    function getGrammarInfo($words) {
        return $this->processAnnotsWithHelper($words, 'getGrammarInfo');
    }

    function getAllFormsWithAncodes($words) {
        return $this->processAnnotsWithHelper($words, 'getAllFormsWithResolvedAncodes', true);
    }

    function getWordDescriptor($word) {
        return $this->processAnnotsWithHelper($words, 'getWordDescriptor', true);
    }

    protected function findWord($words) {
        $unknown_words_annot = '';

        $this->notfound = array();

        list($labels, $finals, $dests) = $this->buildPatriciaTrie($words);

        $annots = array();
        $unknown_words_annot = '';
        $stack = array(0, '', $this->root_trans);
        $stack_idx = 0;

        $fsa = $this->fsa;

        // TODO: Improve this
        while($stack_idx >= 0) {
            $n = $stack[$stack_idx];
            $path = $stack[$stack_idx + 1] . $labels[$n];
            $trans = $stack[$stack_idx + 2];
            $stack_idx -= 3; // TODO: Remove items from stack? (performance!!!)

            $is_final = $finals[$n] > 0;

            $result = false;
            if(false !== $trans && $n > 0) {
                $label = $labels[$n];

                $result = $fsa->walk($trans, $label, $is_final);

                if($GLOBALS['__phpmorphy_strlen']($label) == $result['walked']) {
                    $trans = $result['word_trans'];
                } else {
                    $trans = false;
                }
            }

            if($is_final) {
                if(false !== $trans && isset($result['annot'])) {
                    $annots[$result['annot']][] = $path;
                } else {
                    //$annots[$unknown_words_annot][] = $path;
                    $this->notfound[] = $path;
                }
            }

            if(false !== $dests[$n]) {
                foreach($dests[$n] as $dest) {
                    $stack_idx += 3;
                    $stack[$stack_idx] = $dest;
                    $stack[$stack_idx + 1] = $path;
                    $stack[$stack_idx + 2] = $trans;
                }
            }
        }

        return $annots;
    }

    protected function composeForms($annotsRaw, $onlyBase, $pseudoRoot, $partOfSpeech) {
        $result = array();

        // process found annotations
        foreach($annotsRaw as $annot_raw => $words) {
            if($GLOBALS['__phpmorphy_strlen']($annot_raw) == 0) continue;

            foreach($this->helper->decodeAnnot($annot_raw, $onlyBase) as $annot) {
                if(!($onlyBase || $pseudoRoot)) {
                    $flexias = $this->graminfo->readFlexiaData($annot);
                }

                $cplen = $annot['cplen'];
                $plen = $annot['plen'];
                $flen = $annot['flen'];

                if($partOfSpeech) {
                    $pos_id = $this->helper->extractPartOfSpeech($annot);
                }

                foreach($words as $word) {
                    if($flen) {
                        $base = $GLOBALS['__phpmorphy_substr']($word, $cplen + $plen, -$flen);
                    } else {
                        if($cplen || $plen) {
                            $base = $GLOBALS['__phpmorphy_substr']($word, $cplen + $plen);
                        } else {
                            $base = $word;
                        }
                    }

                    $prefix = $cplen ? $GLOBALS['__phpmorphy_substr']($word, 0, $cplen) : '';

                    if($pseudoRoot) {
                        $result[$word][$base] = 1;
                    } else if($onlyBase) {
                        $form = $prefix . $annot['base_prefix'] . $base . $annot['base_suffix'];

                        $result[$word][$form] = 1;
                    } else if($partOfSpeech) {
                        $result[$word][$pos_id] = 1;
                    } else {
                        for($i = 0, $c = count($flexias); $i < $c; $i += 2) {
                            $form = $prefix . $flexias[$i] . $base . $flexias[$i + 1];
                            $result[$word][$form] = 1;
                        }
                    }
                }
            }
        }

        for($keys = array_keys($result), $i = 0, $c = count($result); $i < $c; $i++) {
            $key = $keys[$i];

            $result[$key] = array_keys($result[$key]);
        }

        return $result;
    }

    protected function buildPatriciaTrie($words) {
        if(!is_array($words)) {
            throw new phpMorphy_Exception("Words must be array");
        }

        sort($words);

        $stack = array();
        $prev_word = '';
        $prev_word_len = 0;
        $prev_lcp = 0;

        $state_labels = array();
        $state_finals = array();
        $state_dests = array();

        $state_labels[] = '';
        $state_finals = '0';
        $state_dests[] = array();

        $node = 0;

        foreach($words as $word) {
            if($word == $prev_word) {
                continue;
            }

            $word_len = $GLOBALS['__phpmorphy_strlen']($word);
            // find longest common prefix
            for($lcp = 0, $c = min($prev_word_len, $word_len); $lcp < $c && $word[$lcp] == $prev_word[$lcp]; $lcp++);

            if($lcp == 0) {
                $stack = array();

                $new_state_id = count($state_labels);

                $state_labels[] = $word;
                $state_finals .= '1';
                $state_dests[] = false;

                $state_dests[0][] = $new_state_id;

                $node = $new_state_id;
            } else {
                $need_split = true;
                $trim_size = 0; // for split

                if($lcp == $prev_lcp) {
                    $need_split = false;
                    $node = $stack[count($stack) - 1];
                } elseif($lcp > $prev_lcp) {
                    if($lcp == $prev_word_len) {
                        $need_split = false;
                    } else {
                        $need_split = true;
                        $trim_size = $lcp - $prev_lcp;
                    }

                    $stack[] = $node;
                } else {
                    $trim_size = $GLOBALS['__phpmorphy_strlen']($prev_word) - $lcp;

                    for($stack_size = count($stack) - 1; ;--$stack_size) {
                        $trim_size -= $GLOBALS['__phpmorphy_strlen']($state_labels[$node]);

                        if($trim_size <= 0) {
                            break;
                        }

                        if(count($stack) < 1) {
                            throw new phpMorphy_Exception('Infinite loop posible');
                        }

                        $node = array_pop($stack);
                    }

                    $need_split = $trim_size < 0;
                    $trim_size = abs($trim_size);

                    if($need_split) {
                        $stack[] = $node;
                    } else {
                        $node = $stack[$stack_size];
                    }
                }

                if($need_split) {
                    $node_key = $state_labels[$node];

                    // split
                    $new_node_id_1 = count($state_labels);
                    $new_node_id_2 = $new_node_id_1 + 1;

                    // new_node_1
                    $state_labels[] = $GLOBALS['__phpmorphy_substr']($node_key, $trim_size);
                    $state_finals .= $state_finals[$node];
                    $state_dests[] = $state_dests[$node];

                    // adjust old node
                    $state_labels[$node] = $GLOBALS['__phpmorphy_substr']($node_key, 0, $trim_size);
                    $state_finals[$node] = '0';
                    $state_dests[$node] = array($new_node_id_1);

                    // append new node, new_node_2
                    $state_labels[] = $GLOBALS['__phpmorphy_substr']($word, $lcp);
                    $state_finals .= '1';
                    $state_dests[] = false;

                    $state_dests[$node][] = $new_node_id_2;

                    $node = $new_node_id_2;
                } else {
                    $new_node_id = count($state_labels);

                    $state_labels[] = $GLOBALS['__phpmorphy_substr']($word, $lcp);
                    $state_finals .= '1';
                    $state_dests[] = false;

                    if(false !== $state_dests[$node]) {
                        $state_dests[$node][] = $new_node_id;
                    } else {
                        $state_dests[$node] = array($new_node_id);
                    }

                    $node = $new_node_id;
                }
            }

            $prev_word = $word;
            $prev_word_len = $word_len;
            $prev_lcp = $lcp;
        }

        return array($state_labels, $state_finals, $state_dests);
    }
}


text:
Jorge Eduardo García (n. 18 de agosto de 2002) es un actor mexicano.

Biografía
Realiza su debut en el año 2005 en Los Plateados de Telemundo junto a Mauricio  Islas. Año 2007 en la empresa Tv Azteca, en Mientras haya vida como Diego. Secretos del alma en el año 2008 fue su segunda telenovela en su carrera. Posteriormente participó en Mujeres asesinas, Amar de nuevo, Una maid en Manhattan y XY. La revista.

El año 2012 actuó en El rostro de la venganza, donde interpreta a Juanito Mercader y también interpreta a Diego Mercader de niño.

fue merecedor de un premio en Premios Tu Mundo 2012 como mejor actor infantil

En el 2013 da vida a Willy Delgado en Santa diabla donde compartirá con los protagonistas Gaby Espino, Aarón Diaz y Carlos Ponce.

Filmografía

Premios y nominaciones

Premios Tu Mundo

People en Español

Referencias

Nacidos en Ciudad de México
Actores de televisión de México
Actores infantiles de México

redpajama_set_name: RedPajamaWikipedia


text:
Emilien Badoux, né le 12 mai 1983, est un snowboardeur suisse.

Il remporte le Freeride World Tour en snowboard en 2014.

Notes et références

Snowboardeur suisse
Naissance en mai 1983


text:
Q: What question has the most answers on it? Of all the questions on SE, what question has the highest number of answers on it?

A: I've made an SEDE query to find the questions with the most answers per site:
SELECT AnswerCount, Id, Score, Body, OwnerUserId, Title, Tags FROM Posts WHERE PostTypeId = 1 ORDER BY AnswerCount DESC

Unfortunately, SEDE doesn't support querying multiple sites at once, and I'm not manually running that query on every single SE site. Someone at SE might be able to, but I'm not aware of a good way for users to do it.
Regardless, I nominate "Sandbox for Proposed Challenges" on Code Golf Meta with 4018 answers currently.

redpajama_set_name: RedPajamaStackExchange


Meghru Vank / S. Hovhannes is on a hill rise in front of the town cemetery, on the east side of the Kadjaran Highway. You will see it on your left as you enter the town, the road to the site set within some new housing. It is about 200 m east of the highway. You can also reach it by taking the main street from Mets Tagh N towards the hospital and top at the cemetery.
The vank ruins are of a 17th c structure, built on a more ancient worship site. The building is in ruins, though substantial portions remain. The gavit is completely destroyed, as are much of the western and northern walls. The dome remains, as do a number of primitive frescoes from the 17th c, mostly on the altar end.
The remains of a ruined 19th century church are on the eastern side of the city, just left from the road directing to the fortress.


In the video below Gabby Wild, aka Gabby Wagner '11, explains her "12 in 12 for 12" campaign to save 12 animal species in danger of extinction through sustainably manufactured fashions.
Animal activist Gabby Wild, B.S. '11, DVM '15 has studied baboons in Kenya, treated elephants in Thailand and followed sled dogs through Alaska. This summer, before beginning veterinary studies, Wild is blogging about her latest effort, riding horses across the Irish countryside to raise funds for the Humane Society of the United States.


text:
Dr. Piercy: 'Only a matter of time' before Omicron variant in Tennessee
Joseph Wenzel, Digital Content Manager
Joe Wenzel
Digital Content Manager
NASHVILLE, TN (WSMV) – As Tennessee sees a slight increase in case trends of COVID-19, Health Commissioner Dr. Lisa Piercy said many of the cases are clustered in the northeast part of the state and among unvaccinated residents.
There are no reports of the Omicron variant in Tennessee at this time. However, the variant has been detected in 15 to 17 states, including border Missouri.
"Only a matter of time before Omicron is here," Piercy said.
Piercy said it is unclear how the mutations will manifest.
"Trends we're seeing, speculation it is more transmissible but makes you less sick. Those are things we don't know yet but will be learning over the next several weeks," Piercy said. "Vaccination is still the best protection."
Piercy said routine variant surveillance means they take a portion of the positive cases and surveillance sequencing for it. The Tennessee Department of Health will announce when the state gets its first case of the variant.
Piercy encourages people to get flu shots and states it is not too late to get one.
"We had a statewide influenza event across the state," Piercy said.
According to Piercy, the state essentially didn't have a flu season last year, and health officials are anticipating a flu season this year. Flu shots go down to six months.
The Tennessee Department of Health is at 21.6% of Tennesseans getting flu shots. It's less than people bringing them in 2019 and 2018.
"I attribute it to the weariness of vaccines," Piercy said.
Tennessee still waiting on first case of "omicron" variant
Many medical professionals anticipate another surge in COVID-19 cases.
Lisa Piercy
Statewide Influenza
Follow Joe Wenzel

redpajama_set_name: RedPajamaCommonCrawl


You can easily care for your teeth. You can do it with a host of simple tips and tricks. Maintaining proper dental care can be easier than you believed possible. Use the tips mentioned in this article to get that great smile without all the work.


Our great experience with Blue Dolphin began before we even set foot on the boat. We called on our first day on Kauai and asked the young lady on the phone for her recommendation for the smoothest day to do a sunset dinner trip up the Na Pali coast. She was super friendly and helpful and recommended Thursday. She was right on! On our trip the captain remarked that it was definitely the smoothest day he'd seen in at least a month. So a huge kudos to her!
1. Expect an amount of banter and joke-telling from the captain and crew. That's just part of the territory on these types of tours. That being said, the captain was appropriately self-deprecating and struck a good balance between being entertaining and letting passengers have a relaxing experience that focused on the stunning views.
2. The safety and boarding procedures were easy and simple. But for the less mobile, know that moving around the boat can be tricky, especially when its choppy. I would hesitate to do the tour if you need a cane or walking assistance.
3. We had a super smooth day and only saw one person on our trip get a little green. But on a windy, choppy day this could be a tough trip. I highly recommend taking Dramamine before you get on board. It's a long trip- why risk feeling sick?
1. A very friendly crew who took great care to make sure everyone on board was fed and watered.
2. There is plenty of food on a dinner trip! Snacks and drinks flowed the whole time, and the buffet dinner was really good! We ran into the caterer as we boarded, and I think the kalau pork they served was the absolute best thing I ate on Kauai. They didn't even come close to running out of food on our trip, even with people (including me) going back for seconds.
3. If you want to drink you'll have plenty of opportunities. They serve beer, wine and mai tais and they had champagne for the sunset.
4. They did a great job chasing wildlife. We got very close to humpback whales (close enough to hear them expelling air), spinner dolphins and bottlenose dolphins. We saw turtles, seals and goats.
5. The boat was clean (even the bathrooms), large and had lots of different types of seating. Loved the bow area with the netting and the ability to walk right on to the front of the hulls.
6. We saw the whole coast, went in close to a sea cave and still made it back in a very speedy four hours. Definitely loved that they took care to be efficient but also made sure to let all of us enjoy everything Na Pali has to offer.
In short, Blue Dolphin did a fantastic job. Definitely a highlight of our trip. I highly recommend them.
More than just a cruise- this is an outstanding experience!


text:
#pragma once

#include "il2cpp-config.h"

#ifndef _MSC_VER
# include <alloca.h>
#else
# include <malloc.h>
#endif

#include <stdint.h>

// System.Collections.Generic.Stack`1<System.Collections.Generic.List`1<UnityEngine.Vector4>>
struct Stack_1_t158878451;
// UnityEngine.Events.UnityAction`1<System.Collections.Generic.List`1<UnityEngine.Vector4>>
struct UnityAction_1_t1249464826;

#include "mscorlib_System_Object4170816371.h"

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winvalid-offsetof"
#pragma clang diagnostic ignored "-Wunused-variable"
#endif

// UnityEngine.UI.ObjectPool`1<System.Collections.Generic.List`1<UnityEngine.Vector4>>
struct  ObjectPool_1_t1154872948  : public Il2CppObject
{
public:
	// System.Collections.Generic.Stack`1<T> UnityEngine.UI.ObjectPool`1::m_Stack
	Stack_1_t158878451 * ___m_Stack_0;
	// UnityEngine.Events.UnityAction`1<T> UnityEngine.UI.ObjectPool`1::m_ActionOnGet
	UnityAction_1_t1249464826 * ___m_ActionOnGet_1;
	// UnityEngine.Events.UnityAction`1<T> UnityEngine.UI.ObjectPool`1::m_ActionOnRelease
	UnityAction_1_t1249464826 * ___m_ActionOnRelease_2;
	// System.Int32 UnityEngine.UI.ObjectPool`1::<countAll>k__BackingField
	int32_t ___U3CcountAllU3Ek__BackingField_3;

public:
	inline static int32_t get_offset_of_m_Stack_0() { return static_cast<int32_t>(offsetof(ObjectPool_1_t1154872948, ___m_Stack_0)); }
	inline Stack_1_t158878451 * get_m_Stack_0() const { return ___m_Stack_0; }
	inline Stack_1_t158878451 ** get_address_of_m_Stack_0() { return &___m_Stack_0; }
	inline void set_m_Stack_0(Stack_1_t158878451 * value)
	{
		___m_Stack_0 = value;
		Il2CppCodeGenWriteBarrier(&___m_Stack_0, value);
	}

	inline static int32_t get_offset_of_m_ActionOnGet_1() { return static_cast<int32_t>(offsetof(ObjectPool_1_t1154872948, ___m_ActionOnGet_1)); }
	inline UnityAction_1_t1249464826 * get_m_ActionOnGet_1() const { return ___m_ActionOnGet_1; }
	inline UnityAction_1_t1249464826 ** get_address_of_m_ActionOnGet_1() { return &___m_ActionOnGet_1; }
	inline void set_m_ActionOnGet_1(UnityAction_1_t1249464826 * value)
	{
		___m_ActionOnGet_1 = value;
		Il2CppCodeGenWriteBarrier(&___m_ActionOnGet_1, value);
	}

	inline static int32_t get_offset_of_m_ActionOnRelease_2() { return static_cast<int32_t>(offsetof(ObjectPool_1_t1154872948, ___m_ActionOnRelease_2)); }
	inline UnityAction_1_t1249464826 * get_m_ActionOnRelease_2() const { return ___m_ActionOnRelease_2; }
	inline UnityAction_1_t1249464826 ** get_address_of_m_ActionOnRelease_2() { return &___m_ActionOnRelease_2; }
	inline void set_m_ActionOnRelease_2(UnityAction_1_t1249464826 * value)
	{
		___m_ActionOnRelease_2 = value;
		Il2CppCodeGenWriteBarrier(&___m_ActionOnRelease_2, value);
	}

	inline static int32_t get_offset_of_U3CcountAllU3Ek__BackingField_3() { return static_cast<int32_t>(offsetof(ObjectPool_1_t1154872948, ___U3CcountAllU3Ek__BackingField_3)); }
	inline int32_t get_U3CcountAllU3Ek__BackingField_3() const { return ___U3CcountAllU3Ek__BackingField_3; }
	inline int32_t* get_address_of_U3CcountAllU3Ek__BackingField_3() { return &___U3CcountAllU3Ek__BackingField_3; }
	inline void set_U3CcountAllU3Ek__BackingField_3(int32_t value)
	{
		___U3CcountAllU3Ek__BackingField_3 = value;
	}
};

#ifdef __clang__
#pragma clang diagnostic pop
#endif

redpajama_set_name: RedPajamaGithub


Kshama Sawant is not a career politician. An activist with Socialist Alternative, she brings a passion for social justice to her work as elected representative of Seattle's working people. An outspoken socialist who has won election and reelection to the City Council, Kshama has taken no corporate campaign money, and has helped win working class victories like the $15/hour minimum wage, renters rights, and a tax on big business to fund affordable housing.


Can't find what you're looking for among all these pages? Just have a comment or question you want to ask? This is your chance! Fill out this short questionnaire and one of our Internet Managers will contact you shortly. We look forward to hearing from you!


text:
Inscriptions from Old Shankill Church 1708-1860
Posted in 18th Century, 19th Century, Church of Ireland / Established Church / Protestant, Genealogy, Roman Catholic Church on April 24, 2011| Leave a Comment »
In the grounds of Shankill Castle there are the ruins of an old church and graveyard. This church would be quite old as it is shown in ruins on the Ordnance Survey maps of Ireland from the 1830s-1840s period.
Towards the end of the 19th century an organization called the Association for the Preservation of the Memorials of the Dead was formed in Ireland. Members of this group spent their spare time visiting graveyards and transcribing the information on headstones, vaults and tombs. Many of these graveyards are in a total state of disrepair today and that makes the journal published by the association very valuable.
In the journal for the year 1900, a Mr. C.J. Hobson writes about how he visited this church and graveyard and transcribed what he could.[1]The relevant genealogical information from his transcriptions are reproduced below.
Elizabeth Ayleward, alias Butler, died 21 October 1708. She had two daughters. Monument erected by her husband Peter Aylward in 1719.
John Gorges Hely Mulhallen of Malcolmville, Carlow died 1 April 1860 age 66. Erected by Frances.
Mary Cody, alias Purcell, died 31 February 1782, age 51 years.
Dennis Ryan, died 9 May 1800, age 70 years.
Erected by Michael Cooke in the memory of his father Thomas Cooke, late of Kellymount, died 16 December 1801, age 70 years. Headstone carved by John Brennan, stone-cutter, Royal Oak
Gregory Murphy, died 29 April 1768, age 21 years.
Michael Murphy, died 26 January 1771, age 60 years. Erected by his son Michael. Also, son Joseph died 26 December 1787, age 45 years.
[1] Hobson, C.J. 1900. Shankill or St. Kill Parish in Journal of the Association for the Preservation of the Memorials of the Dead. Vol.4 No.3 Part 1, pp.432-434.
New York Passenger Lists 1901-1906
Posted in 20th Century, Genealogy, Passenger List, United States on April 16, 2011| Leave a Comment »
In 1891 The Immigration Act became law in the United States. This meant that much more detailed information had to be supplied on passenger lists for ships coming to the United States. For genealogists, this was of course a fantastic development as a place of last residence had to be supplied by immigrants.
Below is a list of five people who came to the US through the port of New York and gave Paulstown as their last place of residence.[1]
James J Byrne Arrived 8 September 1901 Age 24 Single Ship S.S. Celtic Port of Departure Queenstown Occupation Waiter Last Residence Paulstown Previously in New York in 1896 Brother living at 746 10th Avenue
Mary Stokes Arrived 10 August 1898 Age 51 Single Ship S.S. Majestic Port of Departure Liverpool Last Residence Paulstown Final Destination Rochester
William Tracey Arrived 10 August 1898 Age 27 Single Ship S.S. Majestic Port of Departure Liverpool Occupation Farmer Final Destination Buffalo Sister living at West Street, Buffalo, New York
Annie Upton Arrived 6 November 1904 Age 20 Single S.S. Etruria Port of Departure Queenstown Occupation Postulant Final Destination San Antonio Texas Friend of The Superior of Divine Providence Convent San Antonio Texas
Kathleen Upton Arrived 14 October 1906 Age 18 Single S.S. Etruria Port of Departure Queenstown Occupation Postulant Final Destination San Antonio Texas Friend of The Superior of Divine Providence Convent San Antonio Texas
[1] Ancestry.com. New York Passenger Lists, 1820-1957 [database on-line]. Provo, UT, USA: Ancestry.com Operations, Inc., 2010.
Original data: Passenger Lists of Vessels Arriving at New York, New York, 1820-1897; (National Archives Microfilm Publication M237, 675 rolls); Records of the U.S. Customs Service, Record Group 36; National Archives, Washington, D.C.
Passenger and Crew Lists of Vessels Arriving at New York, New York, 1897-1957; (National Archives Microfilm Publication T715, 8892 rolls); Records of the Immigration and Naturalization Service; National Archives, Washington, D.C.
Surnames/Second Names of Paulstown II 1831-1911
Posted in 19th Century, 20th Century, Genealogy on April 10, 2011| 2 Comments »
This is the second post that has a compilation of surnames from the Paulstown area. The first post covered the townlands nearest the village of Paulstown. This post covers the neighbouring townlands to those nearest the village. They are Ballytarsna, Ballyvalden, Butlersgrove, Coolcuttia, Coorleagh, Garryduff and Kilmacahill. Please see the first article, via the link above, for how the names were compiled.
You can open the list as a pdf file via the link below.
Surnames of Paulstown II
Population of Paulstown 1901-2006
Posted in 20th Century, 21st Century, Dictionaries and Surveys on April 3, 2011| Leave a Comment »
The 2011 Census of Ireland will be carried out on 10 April. The most previous census, in 2006, showed that the population of Paulstown increased by over 100%, from 292 to 598.[1] This is a tremendous change for any area in such a short amount of time.
To give some idea of the changing population of the village in the 20th and 21st centuries, I have included some demographic statistics from the Central Statistics Office in Ireland. It is important to remember that these figures cannot be compared against each other when two different area definitions are used.
Currently, I am unaware of what townlands are included in the Paulstown Electoral District. Ancedotal evidence would suggest that it might be just the three townlands where the village is located, namely Shankill, Paulstown and Kellymount.
YEAR POPULATION AREA DEFINITION
2006 598 Electoral District
1996 221[2] Electoral District
1991 234[3] Unknown
1971 459[4] District Electoral Division
1961 239[5] Cluster of 20 houses or more
1951 83[6] Cluster of 20 houses or more
1901 349[10] District Electoral Division
[1] Central Statistics Office. 2006. Alphabetical List of Towns with their population 2002-2006. http://census.cso.ie/Census/TableViewer/tableView.aspx?ReportId=75478 : accessed 16 March 2011
[2] Central Statistics Office. 2002. Population of Towns ordered by County and size, 1996 and 2002 http://www.cso.ie/census/documents/vol1_t5.pdf: accessed 3 March 2011
[3] Central Statistics Office. 1991. Alphabetical list of towns tith their population in 1991 http://www.cso.ie/census/census_1991_results/Volume1/C1991_V1_T20.pdf : accessed 3 March 2011
[4] Central Statistics Office. 1971. Population, area and valuation of each district electoral division, urban district, rural district and county. http://cso.ie/census/census_1971_results/Volume1/C%201971%20V1%20T11.pdf : accessed 3 March 2011
[5] Central Statistics Office. 1961. Alphabetical List of towns with population 1961. http://www.cso.ie/census/census_1961_results/Volume1/C%201961%20VOL%201%20T15.pdf : accessed 3 March 2011
[6] Central Statistics Office. 1951. Alphabetical List of towns with population 1951. http://www.cso.ie/census/census_1951_results/Volume1/C%201951%20V1%20T14.pdf : accessed 03 march 2011
[7] Central Statistics Office. 1946. Alphabetical List of towns with population 1946. http://cso.ie/census/census_1946_results/Volume1/C%201946%20V1%20T13.pdf : accessed 03 march 2011
[8] Central Statistics Office. 1926. Alphabetical List of towns with population 1926. http://www.cso.ie/census/census_1926_results/Volume10/C%201926%20V10%20Chapter%20Appendix%20D.pdf :accessed 03 March 2011
[9]National Archives of Ireland. 2009. 1911 Census of Ireland, Paulstown District Electoral Division. http://census.nationalarchives.ie/search/results.jsp?census_year=1911&surname=&exact=&firstname=&county=Kilkenny&townland=&ded=paulstown&age=&sex=&relationToHead=&religion=&education=&occupation=&marriageStatus=&marriageYears=&childrenBorn=&childrenLiving=&birthplace=&language=&deafdumb=&search=Search&sort=&pageSize=100 : accessed 16 March 2011
[10] National Archives of Ireland. 2009. 1901 Census of Ireland, Paulstown District Electoral Division. http://census.nationalarchives.ie/search/results.jsp?census_year=1901&surname=&firstname=&county=Kilkenny&townland=&ded=paulstown&age=&sex=&search=Search&relationToHead=&religion=&education=&occupation=&marriageStatus=&birthplace=&language=&deafdumb=&marriageYears=&childrenBorn=&childrenLiving= : accessed 16 March 2011


text:
Movies Ranked
Sony Is Releasing a Mini PlayStation Classic With 20 Pre-Loaded Games
By Tony Merevick Published On 09/19/2018
@tonymerevick
By Tony Merevick @tonymerevick Published On 09/19/2018
YouTube/Sony
Just when you thought everyone had left Nintendo to dominate the retro console market with its NES Classic, SNES Classic, and rumored N64 Classic editions, Sony is finally getting in on the nostalgia party with its own miniature console. To the delight of '90s kids everywhere, the company is unleashing what's aptly dubbed as the PlayStation Classic this December -- nearly 25 years after it launched the PlayStation.
Sony officially announced the new console on Tuesday, saying it will come pre-loaded with 20 "legendary" classic PlayStation titles when it arrives in stores just in time for the holiday shopping season on December 3. The tiny console looks just like the OG Playstation (even the controllers and packaging), but is 45% smaller and, of course, doesn't sport an actual disc drive. Sony claims that all of the included games will be playable in the original PS1 formats. The console connects to your TV via an HDMI cable, and it'll come with two controllers for multiplayer game support as well as a USB cable.
Courtesy of Sony
As for the price, Sony said the PlayStation Classic will set you back $99, which is a whopping $20 more than Nintendo's SNES Classic Edition for about the same number of games (the SNES Classic comes with 21). Speaking of the pre-loaded games, the company has only revealed five of the 20 titles so far: Final Fantasy VII, Jumping Flash, Ridge Racer Type 4, Tekken 3, and Wild Arms. It plans to announce the full lineup of games via its blog in the coming weeks.
Although it's probably a bit early to speculate about what availability will be like, Sony will hopefully provide a better supply of the devices than Nintendo has with its retro consoles. The company said retailers in the United States and Canada will offer pre-orders for the PlayStation Classic, which is promising. Keep your eyes peeled for info on that.
Sign up here for our daily Thrillist email and subscribe here for our YouTube channel to get your fix of the best in food/drink/fun.
Tony Merevick is Senior News Editor at Thrillist. Send news tips to news@thrillist.com and follow him on Twitter @tonymerevick.


text:
Q: How to prevent Google Apps Script from changing existing strings (Date, Duration etc.) to time stamps in Google Sheets I am trying to replace employee IDs with employee names in the google sheets using the below script. FYI, the sheet also has other columns including dates and durations etc.
function runReplaceInSheet(){
 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
 var values = sheet.getDataRange().getValues();
 replaceInSheet(values, 'SR1193CS', 'Shyam Reganti');
 sheet.getDataRange().setValues(values);

}
function replaceInSheet(values, to_replace, replace_with) {
 for(var row in values){
 var replaced_values = values[row].map(function(original_value) {
 return original_value.toString().replace(to_replace,replace_with);
});
 values[row] = replaced_values;

}
}
I was able to run the script successfully. However, the dates and durations in the google sheet are converted to time stamps after running the script. I want to prevent that from happening as those fields are used for reporting purposes and is new format is disrupting the reports. For example the existing duration in the sheet "00:37:00" is converted to "Sat Dec 30 1899 00:37:00 GMT-0600 (Central Standard Time)" after running the script.
Please help me and let me know if there is a work around for this.
P.S. I am not a coder.


text:
Shanksville
Remembering 9/11, 14 years later
Emma Patti Harris 0 Comment Nation 9/11, Flight 93, Fort Meade, Pentagon, Shanksville, World Trade Center
Nearly a decade and a half after hijacked planes hit the World Trade Center's twin towers, the Pentagon and a field near Shanksville, Pennsylvania, the anniversary continues to be marked with observances around the country.
The Flight 93 National Memorial near Shanksville in western Pennsylvania is marking the completion of its visitor center, which opened to the public Thursday. At the Pentagon, Secretary of Defense Ash Carter and other officials will join in remembrances for victims' relatives and Pentagon employees.
President Barack Obama is scheduled to observe the anniversary with a visit to Fort Meade, Maryland, in recognition of the military's work to protect the country.
Eleven years later, remembering Sept. 11
The Baltimore Sun 1 Comment The Baltimore Sun 9/11, Arlington, Baltimore, New Jersey, New York City, Pennsylvania, Pentagon, Sept. 11, Shanksville, terrorist attacks, Twin Towers, Washington DC, World Trade Center
On the 11th anniversary of the Sept. 11, 2001 terrorist attacks, Americans across the country are paying tribute to those who lost their lives. Powerful images from memorial services in Shanksville, Pa., Arlington, Va., New York City and elsewhere around the nation show how raw emotions still are, but also how the nation has moved forward but not forgotten.


Feb 14 How Much More is Enough?
As the compensation guy in the room, I tried to figure out a way to explain this from a compensation / behavioral economics / carrot-stick or any other perspective. As it turns out, I had nothing except. "That's crazy!" These were truly insightful words coming from someone who figures out effective pay as a profession.
Later, as I worked on metrics and goals for a long-term incentive plan, this brief conversation came back to me. I had to ask myself, "How much more is enough?" If you have determined that n dollars should be enough to get someone to do what you need, but the need was not filled, is offering n+10% really going to make a difference? And, how would you know?
This is one of the most important issues in variable compensation. In the age of Say on Pay it is even more of a factor in executive compensation. At what point do more dollars simply ensure that someone keeps working, without having making a material change their behavior? Before we decide that more money is the right answers, we need to ask more "why" questions. We have to be better than simply citing survey data as our reasoning.
When carpenters are asked to solve a problem, their first solution has something to do with wood. When a hairstylist is asked how to make someone look better, they usually start with hair. When a compensation professional is given an issue, we often start with money (or its direct equivalent). The next time the opportunity to offer a solution arises make sure you have considered as many "non-money" solutions as possible before you say "add another $100,000". Trust me, your judgment will be lauded and your solutions may be far more effective.
Tell me about a time you added more money, but got nothing new in return. Or, share a time where you decided against more money and got better buy-in than might have seemed possible.


Here is some helpful information that will help your children understand the dental experience better.
Parents and dentists each play an important role in making a child's first dental appointment a positive experience. Any anxiety displayed by parents will be "picked up" by the child. And, an unfriendly dentist can cause unnecessary fear in the child.
Tell your child about the visits but limit the amount of details given. Answer any questions with simple, to-the-point answers. Let the dentist answer more complex or detailed questions. Dentists are trained to describe things to children in a nonthreatening way and in easy-to-understand language. We do not want them to think the visit will be unpleasant, in fact we strive to make it as pleasant as possible. Please do not use words like "hurt" "pain" or "shot". Do not share dental experiences you've had with your children unless of course they were "Good"! Stress to your child how important it is to maintain healthy teeth and gums and that the dentist is a friendly doctor whose job it is to help do this. And most importantly…..let them know we want them to enjoy their time with us and will have a take-home bag filled with goodies when they leave our office!


I'm assuming that the plot of Jane Eyre is familiar to most of you, but if it isn't, be forewarned that I will allude to certain important details. This book is a treasure, and you wouldn't want me to spoil it, so if you haven't read it, I beg you to stop reading this post right now!
The first time I read Jane Eyre was seven or eight years ago. I had just finished up a semester at BYU and was looking forward to some long overdue fun reading. Mike suggested Jane Eyre (believe it or not, it's one of his favorite novels), and I was soon engrossed in the story.
But Mike wouldn't let me read in peace. He kept badgering me with questions: "What's happening now? What did you think about that?," etc. etc, until I finally got so fed up, I asked, "Do you just want me to read it to you?"
He did, and reading it with him made the plot twists and character development even more enjoyable. I can vividly remember the Saturday morning when we woke up and decided to read a little before we got up for the day. We must not have had anything pressing to get to because we ended up just staying in bed until we had finished the book.
When my book club decided to read Jane Eyre, I knew enough time had passed that I wanted to reread it. I'm so glad I did! I was surprised with how much I'd forgotten. Basically, only two things had stuck with me: sweet Helen from Jane's days at boarding school and Mr. Rochester's deranged wife (it's pretty hard to forget something like that even with four pregnancies trying their hardest to wreak havoc on my brain cells).
I find it funny that I could so clearly remember where I was when I finished the book, but I could remember absolutely nothing about the ending itself. It was so delightful to get to enjoy all the surprises of the book the first time and then get to enjoy almost all of them again a second time (bless you, pregnancies).
The story is told from Jane's point of view, as a sort of memoir or autobiography. She begins with her childhood, dismal and tragic due to being left in the care of her uncle's wife, who cares nothing for her and finds every opportunity to chastise and punish her. Then she is sent away to Lowood (a religious boarding school), and despite its being founded on principles of extreme self-denial, Jane finds it a bright and welcome reprieve. There she meets Helen and Miss Temple, both of whom shape and nurture her in profound ways. When she reaches the age of eighteen, and Helen and Miss Temple are both gone, she realizes Lowood has nothing else to offer her, and she applies for a governess position at far away Thornfield Hall.
It is several months before Jane meets the master of the house, Mr. Rochester, and when she does, she is surprised with how easily she forms a friendship with him and how much she enjoys spending time in his company. But Thornfield is full of secrets, and when the floodgates suddenly burst open and Jane discovers all, everything she believes in is threatened, and she discovers just how strong she really is.
And now, I'm finding myself at a loss for what to say next. In the midst of my profound love for this book, my thoughts are reduced to, "Jane is awesome. Jane is awesome. Jane is awesome . . . ," but that does little to convey why I admire her character so much.
"I was experiencing an ordeal: a hand of fiery iron grasped my vitals. Terrible moment: full of struggle, blackness, burning! Not a human being that ever lived could wish to be loved better than I was loved; and him who thus loved me I absolutely worshipped: and I must renounce love and idol. One drear word comprised my intolerable duty - 'Depart!'"
When Mr. Rochester asks, "Is it better to drive a fellow-creature to despair than to transgress a mere human law, no man being injured by the breach?," this reply follows, "This was true: and while he spoke my very conscience and reason turned traitors against me, and charged me with crime in resisting him. They spoke almost as loud as Feeling: and that clamoured wildly. 'Oh, comply!' it said . . . 'Who in the world cares for you?' . . . Still indomitable was the reply: 'I care for myself. The more solitary, the more friendless, the more unsustained I am, the more I will respect myself. I will keep the law given by God; sanctioned by man. I will hold to the principles received by me when I was sane, and not mad--as I am now. Laws and principles are not for the times when there is not temptation: they are for such moments as this . . . "
"I felt veneration for St. John--veneration so strong that its impetus thrust me at once to the point I had so long shunned. I was tempted to cease struggling with him--to rush down the torrent of his will into the gulf of his existence, and there lose my own. I was almost as hard beset by him now as I had been once before, in a different way, by another. I was a fool both times. To have yielded then would have been an error of principle; to have yielded now would have been an error of judgment."
I'll try to resist quoting the rest of the book . . . but only with great difficulty.
I also think this book was far ahead of its time in expressing feelings of passion and love. As much as I love Jane Austen's novels, you have to admit that there's a lot of beating around the bush in them with pages and pages leading up to a two-sentence declaration of love.
Or this one: "No--no--Jane; you must not go. No--I have touched you, heard you, felt the comfort of your presence--the sweetness of your consolation: I cannot give up these joys. I have little left in myself--I must have you . . . My very soul demands you: it will be satisfied, or it will take deadly vengeance on its frame."
The fact that I'm sharing so many quotes is a testament to how much I loved this book. I feel compelled to save them somehow so that I can find them easily and quickly in the future.
When I read this book for the first time, it quickly climbed to the top of my list of favorites. It stayed there through the years, even as my memory of the plot faded, because of what I remember feeling when I read it. Reading it a second time did not lessen my love for it one whit. I've changed in the years that have passed. My life and circumstances have also changed. But Jane still inspires me. The mystery and passion of the plot still thrill me. And the ending still makes my heart swell with happiness. I think it's safe to say this book will remain a favorite for the rest of my life and that this will not be the last time I read it.
I didn't read the post given that I have never read the book and hope to this year (it is on my list). But I did want to take a moment and let you know that when I found your blog a few months ago I stumbled upon your review of Edenbrooke. The is normally not a book I'd ever pick up, but I was intrigued and decided to give it a try. Three days later I am so glad I picked it up. I struggled to put it down, and stayed up until after 1am last night just to finish. It was a bittersweet finish - I loved it, but was so bummed to be done. I'm ready to start it all over again. And I'm looking forward to reading her next book.
Jocelyne - I'm so glad you liked it. Blackmoore was equally difficult to put down! :-) Thanks so much for commenting. It is so fun for me to hear from readers who have found this blog! And I hope you enjoy Jane Eyre (although if you don't, maybe don't mention it to me . . . it will break my heart!).
I read "Jane Eyre" back in high school and remember really liking it, but I'll have to read it again! Like you, I can't remember much of the plot, but I do remember the feelings I had when I read it — and I love how strong of a heroine Jane is, especially for the time period in which the book was written!
I know I'm in a small minority here, but I do not at all like the book Jane Eyre, mostly because I can't stand Jane as a character. What if she really was the bratty, rebellious child and young woman that her aunt and school master found her to be?
It's weird: Wuthering Heights is one of my favorite books, but I somehow feel that Jane Eyre is too dramatic.
Google put Doodle for honoring Charlotte Brontë on her 198th Birthday.
I'm sooooo reading this one soon. I've already purchased the audio.
Pop by and chat about Pride and Prejudice with me. I can't say much about Jane Eyre yet since I haven't read it but I would love to hear your thoughts on P&P.
Aww, I love this post. Jane Eyre is one of my favorites, but the last time I read it was 2007. I was planning on treating myself to a re-read next month for my birthday and you've just convinced me.


Progressive are looking for a experienced Project Engineer to join a consultancy based in North Sydney working with in the North West sector of the Sydney Metro Project. you will be joining a major construction company focused on building on their emerging reputation in Sydney with the development of this project.
The business has a history of working on great regional and rural developments and delivering quality results. taking pride in the employment of high performing and friendly staff.
Coordinating, monitoring and reporting progress of work on site.
For Further information on this position please apply with your updated CV and your best contact number.