From f18d37f0477698ae6a5a71a8541347ac739b64ca Mon Sep 17 00:00:00 2001
From: Niravkumar Patel <niravkumar.patel1989@gmail.com>
Date: Fri, 18 Dec 2015 05:21:53 -0800
Subject: [PATCH] Question Answering System added

---
 src/controllers/components/CrawlComponent.php |   13 +-
 src/executables/ArcTool.php                   |    3 +-
 src/executables/Fetcher.php                   |   16 +-
 src/library/CrawlConstants.php                |    1 +
 src/library/PhraseParser.php                  |   18 +-
 src/library/TripletExtractor.php              | 1118 +++++++++++++++++++++++++
 src/models/PhraseModel.php                    |   37 +
 src/views/elements/PageoptionsElement.php     |    4 +
 8 files changed, 1200 insertions(+), 10 deletions(-)
 create mode 100644 src/library/TripletExtractor.php

diff --git a/src/controllers/components/CrawlComponent.php b/src/controllers/components/CrawlComponent.php
index f4fa887..5e31e04 100644
--- a/src/controllers/components/CrawlComponent.php
+++ b/src/controllers/components/CrawlComponent.php
@@ -1299,7 +1299,7 @@ class CrawlComponent extends Component implements CrawlConstants
                     PhraseParser::extractPhrasesInLists($phrase_string,
                         $lang);
                 $len = strlen($phrase_string);
-                if (PhraseParser::computeSafeSearchScore($word_lists, $len) <
+                if (PhraseParser::computeSafeSearchScore($word_lists['WORD_LIST'], $len) <
                     0.012) {
                     $meta_ids[] = "safe:true";
                     $safe = true;
@@ -1308,13 +1308,20 @@ class CrawlComponent extends Component implements CrawlConstants
                     $safe = false;
                 }
             }
-            if (!isset($word_lists)) {
-                $word_lists = [];
+            if (!isset($word_lists['WORD_LIST'])) {
+                $word_lists['WORD_LIST'] = [];
             }
+
+            if (!isset($word_lists['QUESTION_ANSWER_LIST'])) {
+                $word_lists['QUESTION_ANSWER_LIST'] = [];
+            }
+
             $data["EXTRACTED_WORDS"] = wordwrap($parent->clean(
                 print_r($word_lists, true), "string"), 75, "\n", true);;
             $data["EXTRACTED_META_WORDS"] = wordwrap($parent->clean(
                 print_r($meta_ids, true), "string"), 75, "\n", true);
+            $data["QUESTIONS_TRIPLET"] = wordwrap($parent->clean(
+                print_r($word_lists['QUESTION_ANSWER_LIST'], true), "string"), 75, "\n", true);
         }
         return $data;
     }
diff --git a/src/executables/ArcTool.php b/src/executables/ArcTool.php
index c812283..e4daa7d 100755
--- a/src/executables/ArcTool.php
+++ b/src/executables/ArcTool.php
@@ -925,9 +925,10 @@ class ArcTool implements CrawlConstants
                                 mb_substr($site[self::DESCRIPTION], 0,
                                 C\AD_HOC_TITLE_LENGTH), $site[self::LANG]);
                         }
-                        $word_lists =
+                        $triplet_list = 
                             PhraseParser::extractPhrasesInLists($phrase_string,
                                 $lang);
+                        $word_lists = $triplet_list['WORD_LIST'];
                         $len = strlen($phrase_string);
                         if (PhraseParser::computeSafeSearchScore($word_lists,
                             $len) < 0.012) {
diff --git a/src/executables/Fetcher.php b/src/executables/Fetcher.php
index 6ceeb0d..6896db2 100755
--- a/src/executables/Fetcher.php
+++ b/src/executables/Fetcher.php
@@ -44,6 +44,7 @@ use seekquarry\yioop\library\PhraseParser;
 use seekquarry\yioop\library\processors\PageProcessor;
 use seekquarry\yioop\library\UrlParser;
 use seekquarry\yioop\library\WebArchiveBundle;
+use seekquarry\yioop\library\TripletExtractor;
 
 if (php_sapi_name() != 'cli') {echo "BAD REQUEST"; exit();}
 ini_set("memory_limit", "1200M"); //so have enough memory to crawl sitemaps
@@ -2729,6 +2730,7 @@ class Fetcher implements CrawlConstants
                     $this->video_sources);
             }
             $word_lists = [];
+            $triplet_lists = [];
             /*
                 self::JUST_METAS check to avoid getting sitemaps in results for
                 popular words
@@ -2760,9 +2762,10 @@ class Fetcher implements CrawlConstants
                         mb_substr($site[self::DESCRIPTION], 0,
                         C\AD_HOC_TITLE_LENGTH), $site[self::LANG]);
                 }
-                $word_lists =
+                $triplet_lists =
                     PhraseParser::extractPhrasesInLists($phrase_string,
                         $lang);
+                $word_lists = $triplet_lists['WORD_LIST'];
                 $len = strlen($phrase_string);
                 if (isset($this->programming_language_extension[$lang]) ||
                     PhraseParser::computeSafeSearchScore($word_lists, $len) <
@@ -2803,6 +2806,12 @@ class Fetcher implements CrawlConstants
                 ]->addDocumentWords($doc_keys, self::NEEDS_OFFSET_FLAG,
                 $word_lists, $meta_ids, PhraseParser::$materialized_metas,
                 true, $doc_rank);
+
+            if(isset($triplet_lists['QUESTION_ANSWER_LIST'])){
+                $question_list = $triplet_lists['QUESTION_ANSWER_LIST'];
+                $site[self::QUESTION_TRIPLETS] = $question_list;
+                $this->found_sites[self::SEEN_URLS][$i] = $site;
+            }
             /*
                 $this->no_process_links is set when doing things like
                 mix recrawls. In this case links likely already will appear
@@ -2854,9 +2863,10 @@ class Fetcher implements CrawlConstants
                     $summary[self::LANG] = $lang;
                     $this->found_sites[self::LINK_SEEN_URLS][$part_num][] =
                         $summary;
-                    $link_word_lists =
+                    $link_lists =
                         PhraseParser::extractPhrasesInLists($link_text,
-                        $lang);
+                        $lang); 
+                    $link_word_lists = $link_lists['WORD_LIST'];
                     $link_meta_ids = PhraseParser::calculateLinkMetas($url,
                         $link_host, $link_text, $site_url);
                     if (!isset($this->found_sites[self::INVERTED_INDEX][
diff --git a/src/library/CrawlConstants.php b/src/library/CrawlConstants.php
index 30de80d..a2ae10e 100755
--- a/src/library/CrawlConstants.php
+++ b/src/library/CrawlConstants.php
@@ -229,4 +229,5 @@ interface CrawlConstants
     const IMAGE_LINK = "dr";
     const GRAPH_BASED_SUMMARIZER = 'ds';
     const CENTROID_WEIGHTED_SUMMARIZER = 'dt';
+    const QUESTION_TRIPLETS = 'du';
 }
\ No newline at end of file
diff --git a/src/library/PhraseParser.php b/src/library/PhraseParser.php
index cb7eee6..29be2f1 100755
--- a/src/library/PhraseParser.php
+++ b/src/library/PhraseParser.php
@@ -33,6 +33,7 @@ namespace seekquarry\yioop\library;
 use seekquarry\yioop\configs as C;
 use seekquarry\yioop\models\LocaleModel;
 use seekquarry\yioop\library\processors\PageProcessor;
+use seekquarry\yioop\library\TripletExtractor;
 
 /**
  * For crawlHash
@@ -210,7 +211,8 @@ class PhraseParser
      */
     public static function extractPhrasesAndCount($string, $lang = null)
     {
-        $phrases = self::extractPhrasesInLists($string, $lang);
+        $triplet_list = self::extractPhrasesInLists($string, $lang);
+        $phrases = $triplet_list['WORD_LIST'];
         $phrase_counts = [];
         foreach ($phrases as $term => $positions) {
             $phrase_counts[$term] = count($positions);
@@ -232,7 +234,16 @@ class PhraseParser
         if (!isset(self::$programming_language_map[$lang])) {
             self::canonicalizePunctuatedTerms($string, $lang);
         }
-        return self::extractMaximalTermsAndFilterPhrases($string, $lang);
+        $phrase_list = array();
+        $word_lists = self::extractMaximalTermsAndFilterPhrases($string, $lang);
+        // COMMENT BELOW CODE IN IF TO DISABLE QUESTION ANSWERING SYSTEM
+        if (isset($word_lists)) {
+            $triplets_list = TripletExtractor::storeStatementArraysAsTriplet($word_lists);
+            $word_lists = array_merge($word_lists, $triplets_list['QUESTION_LIST']);
+            $phrase_list['QUESTION_ANSWER_LIST'] = $triplets_list['QUESTION_ANSWER_LIST'];
+        }
+        $phrase_list['WORD_LIST'] = $word_lists;
+        return $phrase_list;
     }
     /**
      * This functions tries to convert acronyms, e-mail, urls, etc into
@@ -1235,8 +1246,9 @@ vaffanculo fok hoer kut lul やりまん 打っ掛け
             return 0;
         }
         if ($unsafe_terms == []) {
-            $unsafe_lists = PhraseParser::extractPhrasesInLists($unsafe_phrase,
+            $triplet_list = PhraseParser::extractPhrasesInLists($unsafe_phrase,
                 "en-US");
+            $unsafe_lists = $triplet_list['WORD_LIST'];
             $unsafe_terms = array_keys($unsafe_lists);
         }
         $num_unsafe_terms = 0;
diff --git a/src/library/TripletExtractor.php b/src/library/TripletExtractor.php
new file mode 100644
index 0000000..cc71e79
--- /dev/null
+++ b/src/library/TripletExtractor.php
@@ -0,0 +1,1118 @@
+<?php
+/**
+ * SeekQuarry/Yioop --
+ * Open Source Pure PHP Search Engine, Crawler, and Indexer
+ *
+ * Copyright (C) 2009 - 2015  Chris Pollett chris@pollett.org
+ *
+ * LICENSE:
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Chris Pollett chris@pollett.org
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2015
+ * @filesource
+ */
+
+namespace seekquarry\yioop\library;
+
+use seekquarry\yioop\configs as C;
+use seekquarry\yioop\library as L;
+
+/**
+ *
+ * @author Nirav Patel
+ * @package seekquarry\yioop\library
+ */
+class TripletExtractor
+{
+
+    /**
+     * Any unique identifier of question word
+     * @question_word string identifier
+     */
+    public static $question_word = "qqque";
+
+    /**
+     * Takes a phrase and tags each term in it with its part of speech.
+     * So each term in the original phrase gets mapped to term~part_of_speech
+     * This tagger is based on a Brill tagger. It makes uses a lexicon
+     * consisting of words from the Brown corpus together with a list of
+     * part of speech tags that that word had in the Brown Corpus. These are
+     * used to get an initial part of speech (in word was not present than
+     * we assume it is a noun). From this a fixed set of rules is used to modify
+     * the initial tag if necessary.
+     *
+     * @param string $phrase text to add parts speech tags to
+     * @return string $tagged_phrase phrase where each term has ~part_of_speech
+     *     appended
+     */
+    public static function tagPartsOfSpeechPhrase($phrase)
+    {
+        preg_match_all("/[\w\d]+/", $phrase, $matches);
+        $tagged_tokens = self::tagTokenizePartOfSpeech($phrase);
+        $tagged_phrase = self::taggedPartOfSpeechTokensToString(
+            $tagged_tokens);
+        return $tagged_phrase;
+    }
+
+    /**
+     * Split input text into terms and output an array with one element
+     * per term, that element consisting of array with the term token
+     * and the part of speech tag.
+     *
+     * @param string $text string to tag and tokenize
+     * @return array of pairs of the form( "token" => token_for_term,
+     *     "tag"=> part_of_speech_tag_for_term) for one each token in $text
+     */
+    public static function tagTokenizePartOfSpeech($text)
+    {
+        static $lex_string = null;
+        if (!$lex_string) {
+            $lex_string = gzdecode(file_get_contents("lexicon.txt.gz"));
+        }
+        preg_match_all("/[\w\d]+/", $text, $matches);
+        $tokens = $matches[0];
+        $nouns = array('NN', 'NNS', 'NNP');
+        $verbs = array('VBD', 'VBP', 'VB');
+        $result = array();
+        $previous = array('token' => -1, 'tag' => -1);
+        $previous_token = -1;
+        sort($tokens);
+        $dictionary = array();
+        /*
+            Notice we sorted the tokens, and notice how we use $cur_pos
+            so only advance forward through $lex_string. So the
+            run time of this is bound by at most one scan of $lex_string
+         */
+        $cur_pos = 0;
+        foreach ($tokens as $token) {
+            $token = strtolower(rtrim($token, "."));
+            $token_pos = stripos($lex_string, "\n" . $token . " ", $cur_pos);
+            if ($token_pos !== false) {
+                $token_pos++;
+                $cur_pos = stripos($lex_string, "\n", $token_pos);
+                $line = trim(substr($lex_string, $token_pos,
+                    $cur_pos - $token_pos));
+                $tag_list = explode(' ', $line);
+                $dictionary[strtolower(rtrim($token, "."))] =
+                    array_slice($tag_list, 1);
+                $cur_pos++;
+            }
+        }
+        // now using our dictionary we tag
+        $i = 0;
+        $tag_list = array();
+        foreach ($matches[0] as $token) {
+            $prev_tag_list = $tag_list;
+            $tag_list = array();
+            // default to a common noun
+            $current = array('token' => $token, 'tag' => 'NN');
+            // remove trailing full stops
+            $token = strtolower(rtrim($token, "."));
+            if (isset($dictionary[$token])) {
+                $tag_list = $dictionary[$token];
+                $current['tag'] = $tag_list[0];
+            }
+            // Converts verbs after 'the' to nouns
+            if ($previous['tag'] == 'DT' && in_array($current['tag'], $verbs)) {
+                $current['tag'] = 'NN';
+            }
+            // Convert noun to number if . appears
+            if ($current['tag'][0] == 'N' && strpos($token, '.') !== false) {
+                $current['tag'] = 'CD';
+            }
+            $ends_with = substr($token, -2);
+            switch ($ends_with) {
+                case 'ed':
+                    // Convert noun to past particle if ends with 'ed'
+                    if ($current['tag'][0] == 'N') {
+                        $current['tag'] = 'VBN';
+                    }
+                    break;
+                case 'ly':
+                    // Anything that ends 'ly' is an adverb
+                    $current['tag'] = 'RB';
+                    break;
+                case 'al':
+                    // Common noun to adjective if it ends with al
+                    if (in_array($current['tag'], $nouns)) {
+                        $current['tag'] = 'JJ';
+                    }
+                    break;
+            }
+            // Noun to verb if the word before is 'would'
+            if ($current['tag'] == 'NN' && $previous_token == 'would') {
+                $current['tag'] = 'VB';
+            }
+            // Convert common noun to gerund
+            if (in_array($current['tag'], $nouns) &&
+                substr($token, -3) == 'ing'
+            ) {
+                $current['tag'] = 'VBG';
+            }
+            //nouns followed by adjectives
+            if (in_array($previous['tag'], $nouns) &&
+                $current['tag'] == 'JJ' && in_array('JJ', $prev_tag_list)
+            ) {
+                $result[$i - 1]['tag'] = 'JJ';
+                $current['tag'] = 'NN';
+            }
+            /* If we get noun noun, and the second can be a verb,
+             * convert to verb; if noun noun and previous could be an
+             * adjective convert to adjective
+             */
+            if (in_array($previous['tag'], $nouns) &&
+                in_array($current['tag'], $nouns)
+            ) {
+                if (in_array('VBN', $tag_list)) {
+                    $current['tag'] = 'VBN';
+                } else {
+                    if (in_array('VBZ', $tag_list)) {
+                        $current['tag'] = 'VBZ';
+                    } else {
+                        if (in_array('JJ', $prev_tag_list)) {
+                            $result[$i - 1]['tag'] = 'JJ';
+                        }
+                    }
+                }
+            }
+            $result[$i] = $current;
+            $i++;
+            $previous = $current;
+            $previous_token = $token;
+        }
+        return $result;
+    }
+
+    /**
+     * Takes an array of pairs (token, tag) that came from phrase
+     * and builds a new phrase where terms look like token~tag.
+     *
+     * @param array $tagged_tokens array of pairs as might come from tagTokenize
+     * @return string $tagged_phrase a phrase with terms in the format token~tag
+     */
+    public static function taggedPartOfSpeechTokensToString($tagged_tokens)
+    {
+        $tagged_phrase = "";
+        $simplified_parts_of_speech = array(
+            "NN" => "NN",
+            "NNS" => "NN",
+            "NNP" => "NN",
+            "NNPS" => "NN",
+            "PRP" => "NN",
+            'PRP$' => "NN",
+            "WP" => "NN",
+            "VB" => "VB",
+            "VBD" => "VB",
+            "VBN" => "VB",
+            "VBP" => "VB",
+            "VBZ" => "VB",
+            "JJ" => "AJ",
+            "JJR" => "AJ",
+            "JJS" => "AJ",
+            "RB" => "AV",
+            "RBR" => "AV",
+            "RBS" => "AV",
+            "WRB" => "AV"
+        );
+        foreach ($tagged_tokens as $t) {
+            $tag = trim($t['tag']);
+            $tag = (isset($simplified_parts_of_speech[$tag])) ?
+                $simplified_parts_of_speech[$tag] : $tag;
+            $tagged_phrase .= $t['token'] . "~" . $tag . " ";
+        }
+        return $tagged_phrase;
+    }
+
+    public static function partOfSpeechTagger_Brill($text)
+    {
+        static $dict = null;
+        $lexicon = C\LOCALE_DIR . "/en_US/resources/lexicon.txt";
+
+        if (!$dict) {
+            $fh = fopen($lexicon, 'r');
+            while ($line = fgets($fh)) {
+                $tags = explode(' ', $line);
+                $dict[strtolower(array_shift($tags))] = $tags;
+            }
+            fclose($fh);
+        }
+
+        preg_match_all("/[\w\d\.]+/", $text, $matches);
+        $nouns = array('NN', 'NNS');
+
+        $return = array();
+        $i = 0;
+        foreach ($matches[0] as $token) {
+            // default to a common noun
+            $return[$i] = array('token' => $token, 'tag' => 'NN');
+
+            // remove trailing full stops
+            if (substr($token, -1) == '.') {
+                $token = preg_replace('/\.+$/', '', $token);
+            }
+
+            // get from dict if set
+            if (isset($dict[strtolower($token)])) {
+                $return[$i]['tag'] = trim($dict[strtolower($token)][0]);
+            }
+
+            // Converts verbs after 'the' to nouns
+            if ($i > 0) {
+                if ($return[$i - 1]['tag'] == 'DT' &&
+                    in_array($return[$i]['tag'],
+                        array('VBD', 'VBP', 'VB'))
+                ) {
+                    $return[$i]['tag'] = 'NN';
+                }
+            }
+
+            // Convert noun to number if . appears
+            if ($return[$i]['tag'][0] == 'N' && strpos($token, '.') !== false) {
+                $return[$i]['tag'] = 'CD';
+            }
+
+            // Convert noun to past particile if ends with 'ed'
+            if ($return[$i]['tag'][0] == 'N' && substr($token, -2) == 'ed') {
+                $return[$i]['tag'] = 'VBN';
+            }
+
+            // Anything that ends 'ly' is an adverb
+            if (substr($token, -2) == 'ly') {
+                $return[$i]['tag'] = 'RB';
+            }
+
+            // Common noun to adjective if it ends with al
+            if (in_array($return[$i]['tag'], $nouns)
+                && substr($token, -2) == 'al'
+            ) {
+                $return[$i]['tag'] = 'JJ';
+            }
+
+            // Noun to verb if the word before is 'would'
+            if ($i > 0) {
+                if ($return[$i]['tag'] == 'NN'
+                    && strtolower($return[$i - 1]['token']) == 'would'
+                ) {
+                    $return[$i]['tag'] = 'VB';
+                }
+            }
+
+            // Convert noun to plural if it ends with an s
+            if ($return[$i]['tag'] == 'NN' && substr($token, -1) == 's') {
+                $return[$i]['tag'] = 'NNS';
+            }
+
+            // Convert common noun to gerund
+            if (in_array($return[$i]['tag'], $nouns)
+                && substr($token, -3) == 'ing'
+            ) {
+                $return[$i]['tag'] = 'VBG';
+            }
+
+            // If we get noun, and the second can be a verb, convert to verb
+            if ($i > 0) {
+                if (in_array($return[$i]['tag'], $nouns)
+                    && in_array($return[$i - 1]['tag'], $nouns)
+                    && isset($dict[strtolower($token)])
+                ) {
+                    if (in_array('VBN', $dict[strtolower($token)])) {
+                        $return[$i]['tag'] = 'VBN';
+                    } else {
+                        if (in_array('VBZ',
+                            $dict[strtolower($token)])) {
+                            $return[$i]['tag'] = 'VBZ';
+                        }
+                    }
+                }
+            }
+            $i++;
+        }
+        return $return;
+    }
+
+    /**
+     * @param $tagger_array tagged array from part of speech tagger
+     * @return array formed tree array
+     */
+    public static function generateParseTreeUsingRDP($tagger_array)
+    {
+        $tree = array();
+        $tree = ["cur_node" => 0];
+
+        $tree_np = TripletExtractor::extractNPUsingRDP($tagger_array, $tree);
+        $tree = ["cur_node" => $tree_np['cur_node']];
+        $tree_vp = TripletExtractor::extractVPUsingRDP($tagger_array, $tree);
+
+        if ($tree == $tree_vp) {
+            return $tree;
+        }
+
+        $tree['cur_node'] = $tree_vp['cur_node'];
+        unset($tree_np['cur_node']);
+        unset($tree_vp['cur_node']);
+        $tree['NP'] = $tree_np['NP'];
+        $tree['VP'] = $tree_vp['VP'];
+
+        return $tree;
+    }
+
+    /**
+     * @param $tagger_array tagged array
+     * @param $tree current tree
+     * @return mixed tree with NP node
+     */
+    public static function extractNPUsingRDP($tagger_array, $tree)
+    {
+        $NP = $tree['cur_node'];
+
+        $tree_dt = ['cur_node' => $NP];
+        $tree_dt = TripletExtractor::extractDTUsingRDP($tagger_array, $tree_dt);
+
+        $tree_jj = ['cur_node' => $tree_dt['cur_node']];
+        $tree_jj = TripletExtractor::extractJJUsingRDP($tagger_array, $tree_jj);
+
+        $tree_nn = ['cur_node' => $tree_jj['cur_node']];
+        $tree_nn = TripletExtractor::extractNNUsingRDP($tagger_array, $tree_nn);
+
+        if ($NP == $tree_nn['cur_node']) {
+            $tree['NP'] = "";
+        } else {
+            $cur_node = $tree_nn['cur_node'];
+
+            unset($tree_dt['cur_node']);
+            $tree_new_sub['DT'] = $tree_dt;
+
+            unset($tree_jj['cur_node']);
+            $tree_new_sub['JJ'] = $tree_jj;
+
+            unset($tree_nn['cur_node']);
+            $tree_new_sub['NN'] = $tree_nn;
+
+            $tree_new['cur_node'] = $cur_node;
+            $tree_new['NP'] = $tree_new_sub;
+
+            return $tree_new;
+        }
+        return $tree;
+    }
+
+    /**
+     * @param $tagger_array pos tagged array
+     * @param $tree current tree
+     * @return mixed DT added tree
+     */
+    public static function extractDTUsingRDP($tagger_array, $tree)
+    {
+        if (isset($tagger_array[$tree['cur_node']]['tag']) &&
+            ("DT" == $tagger_array[$tree['cur_node']]['tag'])) {
+            $tree['DT'] = $tagger_array[$tree['cur_node']]['token'];
+            $tree['cur_node'] = $tree['cur_node'] + 1;
+            return $tree;
+        }
+        return $tree;
+    }
+
+    /**
+     * @param $tagger_array POS tagged array
+     * @param $tree current tree
+     * @return mixed JJ added tree
+     */
+    public static function extractJJUsingRDP($tagger_array, $tree)
+    {
+        $adjective_string = "";
+        while (isset($tagger_array[$tree['cur_node']]['tag']) &&
+            ("JJ" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "JJR" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "JJS" == $tagger_array[$tree['cur_node']]['tag'])) {
+            $adjective_string .= " " .
+                $tagger_array[$tree['cur_node']]['token'];
+            $tree['cur_node'] = $tree['cur_node'] + 1;
+        }
+
+        if ("" != $adjective_string) {
+            $tag = "JJ";
+            $tree[$tag] = $adjective_string;
+        }
+        return $tree;
+    }
+
+    /**
+     * @param $tagger_array POS tagged array
+     * @param $tree current generated tree
+     * @return mixed NN added tree
+     */
+    public static function extractNNUsingRDP($tagger_array, $tree)
+    {
+        //Combining multiple noun into one
+        $noun_string = "";
+        while (isset($tagger_array[$tree['cur_node']]['tag']) && (
+            "NN" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "NNS" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "NNP" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "NNPS" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "PRP" == $tagger_array[$tree['cur_node']]['tag'])) {
+            $noun_string .= " " . $tagger_array[$tree['cur_node']]['token'];
+            $tree['cur_node'] = $tree['cur_node'] + 1;
+        }
+
+        if ("" != $noun_string) {
+            $tag = "NN";
+            $tree[$tag] = $noun_string;
+        }
+
+        // Checking for preposition
+        if (isset($tagger_array[$tree['cur_node']]['tag']) &&
+            ("IN" == $tagger_array[$tree['cur_node']]['tag'])) {
+            $tag = $tagger_array[$tree['cur_node']]['tag'];
+            $preposition_string = $tagger_array[$tree['cur_node']]['token'];
+            $tree[$tag] = $preposition_string;
+            $tree['cur_node'] = $tree['cur_node'] + 1;
+        }
+
+        if (isset($tagger_array[$tree['cur_node']]['tag']) &&
+            ("DT" == $tagger_array[$tree['cur_node']]['tag'])) {
+            $tree['DT_1'] = $tagger_array[$tree['cur_node']]['token'];
+            $tree['cur_node'] = $tree['cur_node'] + 1;
+        }
+
+        $adjective_string = "";
+        while (isset($tagger_array[$tree['cur_node']]['tag']) &&
+            ("JJ" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "JJR" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "JJS" == $tagger_array[$tree['cur_node']]['tag'])) {
+            $adjective_string .= " " .
+                $tagger_array[$tree['cur_node']]['token'];
+            $tree['cur_node'] = $tree['cur_node'] + 1;
+        }
+
+        if ("" != $adjective_string) {
+            $tag = "JJ_1";
+            $tree[$tag] = $adjective_string;
+        }
+
+        $propernoun_string = "";
+        while (isset($tagger_array[$tree['cur_node']]['tag']) &&
+            ("NN" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "NNS" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "NNP" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "NNPS" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "PRP" == $tagger_array[$tree['cur_node']]['tag'])) {
+            $propernoun_string .= " " .
+                $tagger_array[$tree['cur_node']]['token'];
+            $tree['cur_node'] = $tree['cur_node'] + 1;
+        }
+
+        if ("" != $propernoun_string) {
+            $tag = "NNP";
+            $tree[$tag] = $propernoun_string;
+        }
+        return $tree;
+    }
+
+    /**
+     * @param $tagger_array POS tagged array
+     * @param $tree current tree
+     * @return mixed VP added tree
+     */
+    public static function extractVPUsingRDP($tagger_array, $tree)
+    {
+        $VP = $tree['cur_node'];
+        $tree_vp = ['cur_node' => $VP];
+
+        $tree_vb = ['cur_node' => $tree_vp['cur_node']];
+        $tree_vb = TripletExtractor::extractVBUsingRDP($tagger_array, $tree_vb);
+
+        if ($VP == $tree_vb['cur_node']) {
+            return $tree;
+        }
+
+        $tree_np = ['cur_node' => $tree_vb['cur_node']];
+        $tree_np = TripletExtractor::extractNPUsingRDP($tagger_array, $tree_np);
+
+        if ($VP == $tree_np['cur_node']) {
+
+        } else {
+            $cur_node = $tree_np['cur_node'];
+            unset($tree_vb['cur_node']);
+            unset($tree_np['cur_node']);
+            $tree_new_sub['VB'] = $tree_vb;
+            $tree_new_sub['NP'] = $tree_np['NP'];
+            $tree_new['cur_node'] = $cur_node;
+            $tree_new['VP'] = $tree_new_sub;
+            return $tree_new;
+        }
+        return $tree;
+    }
+
+    /**
+     * @param $tagger_array POS tagged tree
+     * @param $tree current tree
+     * @return mixed VB added tree
+     */
+    public static function extractVBUsingRDP($tagger_array, $tree)
+    {
+        while (isset($tagger_array[$tree['cur_node']]['tag']) &&
+            ("VB" != $tagger_array[$tree['cur_node']]['tag'] &&
+            "VBD" != $tagger_array[$tree['cur_node']]['tag'] &&
+            "VBG" != $tagger_array[$tree['cur_node']]['tag'] &&
+            "VBN" != $tagger_array[$tree['cur_node']]['tag'] &&
+            "VBP" != $tagger_array[$tree['cur_node']]['tag'] &&
+            "VBZ" != $tagger_array[$tree['cur_node']]['tag'])) {
+            $tree['cur_node'] = $tree['cur_node'] + 1;
+        }
+
+        $verb_string = "";
+        while (isset($tagger_array[$tree['cur_node']]['tag']) &&
+            ("VB" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "VBD" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "VBG" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "VBN" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "VBP" == $tagger_array[$tree['cur_node']]['tag'] ||
+            "VBZ" == $tagger_array[$tree['cur_node']]['tag'])) {
+            $verb_string .= " " . $tagger_array[$tree['cur_node']]['token'];
+            $tree['cur_node'] = $tree['cur_node'] + 1;
+        }
+
+        if ("" != $verb_string) {
+            $tag = "VB";
+            $tree[$tag] = $verb_string;
+        }
+        return $tree;
+    }
+
+    /**
+     * @param $tree fully generated tree
+     * @return array triplet array
+     */
+    public static function extractTriplet($tree)
+    {
+        $triplet = array();
+        $triplet['subject'] = TripletExtractor::extractSubjectFromTree($tree);
+        $triplet['predicate'] =
+            TripletExtractor::extractPredicateFromTree($tree);
+        $triplet['object'] = TripletExtractor::extractObjectFromTree($tree);
+        return $triplet;
+    }
+
+    /**
+     * @param $triplet_tree any statement
+     * @return array processed triplet
+     */
+    public static function processTripletForStorage($triplet_tree)
+    {
+        $processed_triplet = array();
+        $processed_triplet['RAW'] =
+            TripletExtractor::getRawTripletForStorage($triplet_tree);
+        $processed_triplet['FEATURED'] =
+            TripletExtractor::getFeaturedTripletForStorage($triplet_tree);
+        return $processed_triplet;
+    }
+
+    /**
+     * @param $triplet_tree triplet array
+     * @return array raw triplet array
+     */
+    public static function getRawTripletForStorage($triplet_tree)
+    {
+        $raw_triplet = array();
+        $question_answer_triplet = array();
+        if (isset($triplet_tree['subject']['RAW'])
+            && isset($triplet_tree['predicate']['RAW'])
+            && isset($triplet_tree['object']['RAW'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet_tree['subject']['RAW'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet_tree['predicate']['RAW'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet_tree['object']['RAW'])
+        ) {
+
+            $SUBJECT = trim($triplet_tree['subject']['RAW']);
+            $PREDICATE = trim($triplet_tree['predicate']['RAW']);
+            $OBJECT = trim($triplet_tree['object']['RAW']);
+
+            $raw_triplet['SUBJECT'] =
+                TripletExtractor::$question_word . " " . $PREDICATE . " " . $OBJECT;
+            $raw_triplet['PREDICATE'] =
+                $SUBJECT . " " . TripletExtractor::$question_word . " " . $OBJECT;
+            $raw_triplet['OBJECT'] =
+                $SUBJECT . " " . $PREDICATE . " " . TripletExtractor::$question_word;
+
+            $question_answer_triplet[TripletExtractor::$question_word . " " . $PREDICATE . " " . $OBJECT] = $SUBJECT;
+            $question_answer_triplet[$SUBJECT . " " . TripletExtractor::$question_word . " " . $OBJECT] = $PREDICATE;
+            $question_answer_triplet[$SUBJECT . " " . $PREDICATE . " " . TripletExtractor::$question_word] = $OBJECT;
+            $raw_triplet['QUESTION_ANSWER_LIST'] = $question_answer_triplet;
+        }
+        return $raw_triplet;
+    }
+
+    /**
+     * @param $triplet_tree triplet array
+     * @return array featured triplet array
+     */
+    public static function getFeaturedTripletForStorage($triplet_tree)
+    {
+        $featured_triplet = array();
+        $question_answer_triplet = array();
+        if (isset($triplet_tree['subject']['FEATURED'])
+            && isset($triplet_tree['predicate']['FEATURED'])
+            && isset($triplet_tree['object']['FEATURED'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet_tree['subject']['FEATURED'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet_tree['predicate']['FEATURED'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet_tree['object']['FEATURED'])
+        ) {
+            $SUBJECT = trim($triplet_tree['subject']['FEATURED']);
+            $PREDICATE = trim($triplet_tree['predicate']['FEATURED']);
+            $OBJECT = trim($triplet_tree['object']['FEATURED']);
+
+            $featured_triplet['SUBJECT'] =
+                TripletExtractor::$question_word . " " . $PREDICATE . " " . $OBJECT;
+            $featured_triplet['PREDICATE'] =
+                $SUBJECT . " " . TripletExtractor::$question_word . " " . $OBJECT;
+            $featured_triplet['OBJECT'] =
+                $SUBJECT . " " . $PREDICATE . " " . TripletExtractor::$question_word;
+
+            $question_answer_triplet[TripletExtractor::$question_word . " " . $PREDICATE . " " . $OBJECT] = $SUBJECT;
+            $question_answer_triplet[$SUBJECT . " " . TripletExtractor::$question_word . " " . $OBJECT] = $PREDICATE;
+            $question_answer_triplet[$SUBJECT . " " . $PREDICATE . " " . TripletExtractor::$question_word] = $OBJECT;
+
+            $featured_triplet['QUESTION_ANSWER_LIST'] = $question_answer_triplet;
+        }
+        return $featured_triplet;
+    }
+
+    /**
+     * @param $string any string
+     * @return bool true if null of empty string
+     */
+    public static function IsNullOrEmptyString($string)
+    {
+        return (!isset($string) || trim($string) === '');
+    }
+
+    /**
+     * @param $tree generated tree
+     * @return array subject array
+     */
+    public static function extractSubjectFromTree($tree)
+    {
+        $subject = array();
+        if (isset($tree['NP']) && $tree['NP'] != null) {
+            $tree_np = $tree['NP'];
+            $value = TripletExtractor::extractFirstNounFromNPTree($tree_np);
+            $subject['RAW'] = $value;
+            $featured_subject = "";
+            $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($tree_np));
+            foreach ($it as $v) {
+                $featured_subject .= $v . " ";
+            }
+            $subject['FEATURED'] = $featured_subject;
+        } else {
+            $subject['RAW'] = "";
+            $subject['FEATURED'] = "";
+        }
+        return $subject;
+    }
+
+    /**
+     * @param $tree generated tree
+     * @return array predicate array
+     */
+    public static function extractPredicateFromTree($tree)
+    {
+        $predicate = array();
+        if (isset($tree['VP']) && $tree['VP'] != null) {
+            $tree_vp = $tree['VP'];
+            $value = TripletExtractor::extractDeepestVerbFromVBTree($tree_vp);
+            $predicate['RAW'] = $value;
+            $featured_predicate = "";
+            if (isset($tree_vp['VB']) && $tree_vp['VB'] != null) {
+                $tree_vb = $tree_vp['VB'];
+                $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($tree_vb));
+                foreach ($it as $v) {
+                    $featured_predicate .= $v . " ";
+                }
+                $predicate['FEATURED'] = $featured_predicate;
+            }
+        } else {
+            $predicate['RAW'] = "";
+            $predicate['FEATURED'] = "";
+        }
+        return $predicate;
+    }
+
+    /**
+     * @param $tree generated tree
+     * @return array object array
+     */
+    public static function extractObjectFromTree($tree)
+    {
+        $object = array();
+        if (isset($tree['VP']) && $tree['VP'] != null) {
+            $tree_vp = $tree['VP'];
+            if (isset($tree_vp['NP']) && $tree_vp['NP'] != null) {
+                $nb = $tree_vp['NP'];
+                $value = TripletExtractor::extractFirstNounFromNPTree($nb);
+                $object['RAW'] = $value;
+                $featured_object = "";
+                $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($nb));
+                foreach ($it as $v) {
+                    $featured_object .= $v . " ";
+                }
+                $object['FEATURED'] = $featured_object;
+            } else {
+                $object['RAW'] = "";
+                $object['FEATURED'] = "";
+            }
+        } else {
+            $object['RAW'] = "";
+            $object['FEATURED'] = "";
+        }
+        return $object;
+    }
+
+    /**
+     * @param $tree_np noun phrase subtree
+     * @return string first noun
+     */
+    public static function extractFirstNounFromNPTree($tree_np)
+    {
+        if (isset($tree_np['NN']) && $tree_np['NN'] != null) {
+            $nn = $tree_np['NN'];
+            if (isset($nn['NN']) && $nn['NN'] != null) {
+                $nn = $nn['NN'];
+                return $nn;
+            }
+        }
+        return "";
+    }
+
+    /**
+     * @param $tree_vp verb phrase subtree
+     * @return string deepest verb
+     */
+    public static function extractDeepestVerbFromVBTree($tree_vp)
+    {
+        if (isset($tree_vp['VB']) && $tree_vp['VB'] != null) {
+            $vb = $tree_vp['VB'];
+            if ($vb['VB'] != null) {
+                $vb = $vb['VB'];
+                return $vb;
+            }
+        }
+        return "";
+    }
+
+    /**
+     * @param $tree generated tree
+     * @return array attributes array
+     */
+    public static function extractAttributes($tree)
+    {
+        $attribute_map = array();
+        if (isset($tree['JJ']) && count($tree['JJ']) > 0) {
+            $attribute_map['JJ'] = $tree['JJ']['JJ'];
+        }
+
+        if (isset($tree['NN']) && count($tree['NN']) > 0) {
+
+            $nn_tree = $tree['NN'];
+
+            if (isset($nn_tree['IN']) && count($nn_tree['IN']) > 0) {
+                $attribute_map['IN'] = $nn_tree['IN'];
+            }
+
+            if (isset($nn_tree['JJ_1']) && count($nn_tree['JJ_1']) > 0) {
+                $attribute_map['JJ_1'] = $nn_tree['JJ_1'];
+            }
+
+            if (isset($nn_tree['NNP']) && count($nn_tree['NNP']) > 0) {
+                $attribute_map['NNP'] = $nn_tree['NNP'];
+            }
+        }
+        return $attribute_map;
+    }
+
+    /**
+     * @param $statement any statement
+     * @return array processed triplet
+     */
+    public static function storeStatementAsTriplet($statement)
+    {
+        try {
+            $tagged_statement =
+                TripletExtractor::partOfSpeechTagger_Brill($statement);
+            $statement_tree =
+                TripletExtractor::generateParseTreeUsingRDP($tagged_statement);
+            $triplet_tree = TripletExtractor::extractTriplet($statement_tree);
+            return TripletExtractor::processTripletForStorage($triplet_tree);
+        } catch (\Exception $e) {
+
+        }
+    }
+
+    /**
+     * @param $statement_array array of statements
+     * @return array list of triplets
+     */
+    public static function storeStatementArraysAsTriplet($statement_array)
+    {
+        $triplets_list = array();
+        $question_list = array();
+        $question_answer_list = array();
+        foreach ($statement_array as $key => $value) {
+            try {
+                if (str_word_count($key) >= 3) {
+                    $extracted_triplet = TripletExtractor::storeStatementAsTriplet($key);
+
+                    if (isset($extracted_triplet['RAW']) &&
+                        sizeof($extracted_triplet['RAW']) > 0) {
+                        $question_list[$extracted_triplet['RAW']['SUBJECT']] = $value;
+                        $question_list[$extracted_triplet['RAW']['PREDICATE']] = $value;
+                        $question_list[$extracted_triplet['RAW']['OBJECT']] = $value;
+                        $question_answer_list = array_merge($question_answer_list,
+                            $extracted_triplet['RAW']['QUESTION_ANSWER_LIST']);
+                    }
+
+                    if (isset($extracted_triplet['FEATURED']) &&
+                        sizeof($extracted_triplet['FEATURED']) > 0) {
+                        $question_list[$extracted_triplet['FEATURED']['SUBJECT']] = $value;
+                        $question_list[$extracted_triplet['FEATURED']['PREDICATE']] = $value;
+                        $question_list[$extracted_triplet['FEATURED']['OBJECT']] = $value;
+                        $question_answer_list = array_merge($question_answer_list,
+                            $extracted_triplet['FEATURED']['QUESTION_ANSWER_LIST']);
+                    }
+                }
+            } catch (\Exception $e) {
+                echo 'Caught exception: ', $e->getMessage(), "\n";
+                continue;
+            }
+        }
+        $triplets_list['QUESTION_LIST'] = $question_list;
+        $triplets_list['QUESTION_ANSWER_LIST'] = $question_answer_list;
+        return $triplets_list;
+    }
+
+    /**
+     * @param $question_string question string
+     * @return array question triplet
+     */
+    public static function questionParser($question_string)
+    {
+        $question_string_tagged = TripletExtractor::partOfSpeechTagger_Brill($question_string);
+        $index = 0;
+        $generated_question_array = array();
+
+        if (isset($question_string_tagged[$index]) &&
+            ("WRB" == trim($question_string_tagged[$index]['tag']) ||
+                "WP" == trim($question_string_tagged[$index]['tag']))
+        ) {
+
+            if ("WHO" == strtoupper(trim($question_string_tagged[$index]['token']))) {
+                $index = $index + 1;
+                $generated_question_array =
+                    TripletExtractor::parseWHOQuestion($question_string_tagged, $index);
+            } else {
+                if ("WHERE" == strtoupper(trim($question_string_tagged[$index]['token'])) ||
+                    "WHEN" == strtoupper(trim($question_string_tagged[$index]['token'])) ||
+                    "WHAT" == strtoupper(trim($question_string_tagged[$index]['token']))
+                ) {
+                    $index = $index + 1;
+                    $generated_question_array =
+                        TripletExtractor::parseWHPlusQuestion_New($question_string_tagged,
+                        $index);
+                }
+            }
+        }
+        return $generated_question_array;
+    }
+
+    /**
+     * @param $question_string_tagged tagged question statement
+     * @param $index current index in statement
+     * @return array parsed triplet
+     */
+    public static function parseWHOQuestion($question_string_tagged, $index)
+    {
+        $generated_question_array = array();
+        $tree = ["cur_node" => $index];
+        $tree['NP'] = "WHO";
+        $triplet = array();
+
+        $tree_vp = TripletExtractor::extractVPUsingRDP($question_string_tagged, $tree);
+        $triplet['predicate'] = TripletExtractor::extractPredicateFromTree($tree_vp);
+        $triplet['object'] = TripletExtractor::extractObjectFromTree($tree_vp);
+
+        if (isset($triplet['object']['RAW'])
+            && isset($triplet['predicate']['RAW'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['object']['RAW'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['predicate']['RAW'])
+        ) {
+            $generated_question_array['RAW']['1'] =
+                trim($triplet['object']['RAW']) .
+                " " . trim($triplet['predicate']['RAW']) .
+                " " . trim(TripletExtractor::$question_word);
+            $generated_question_array['RAW']['2'] =
+                trim(TripletExtractor::$question_word) .
+                " " . trim($triplet['predicate']['RAW']) .
+                " " . trim($triplet['object']['RAW']);
+        }
+
+        if (isset($triplet['object']['FEATURED'])
+            && isset($triplet['predicate']['FEATURED'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['object']['FEATURED'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['predicate']['FEATURED'])
+        ) {
+            $generated_question_array['FEATURED']['1'] =
+                trim($triplet['object']['FEATURED']) .
+                " " . trim($triplet['predicate']['FEATURED']) .
+                " " . trim(TripletExtractor::$question_word);
+            $generated_question_array['FEATURED']['2'] =
+                trim($triplet['object']['FEATURED']) .
+                " " . trim($triplet['predicate']['FEATURED']) .
+                " " . trim(TripletExtractor::$question_word);
+        }
+        return $generated_question_array;
+    }
+
+    /**
+     * @param $question_string_tagged tagged question statement
+     * @param $index current index in statement
+     * @return array parsed triplet
+     */
+    public static function parseWHPlusQuestion($question_string_tagged, $index)
+    {
+        $generated_question_array = array();
+        if (isset($question_string_tagged[$index])
+            && ("VBD" == trim($question_string_tagged[$index]['tag']))) {
+            $index = $index + 1;
+        }
+
+        $tree = ["cur_node" => $index];
+        $tree['NP'] = "WHPlus";
+
+        $triplet = array();
+        $tree_np = TripletExtractor::extractNPUsingRDP($question_string_tagged, $tree);
+        $triplet['subject'] = TripletExtractor::extractSubjectFromTree($tree_np);
+        $tree_vp = TripletExtractor::extractVPUsingRDP($question_string_tagged, $tree);
+        $triplet['predicate'] = TripletExtractor::extractPredicateFromTree($tree_vp);
+        $triplet['object'] = TripletExtractor::extractObjectFromTree($tree_vp);
+
+        if (isset($triplet['object']['RAW'])
+            && isset($triplet['predicate']['RAW'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['object']['RAW'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['predicate']['RAW'])
+        ) {
+            $generated_question_array['RAW']['1'] =
+                trim($triplet['object']['RAW']) .
+                " " . trim($triplet['predicate']['RAW']) .
+                " " . trim(TripletExtractor::$question_word);
+            $generated_question_array['RAW']['2'] =
+                trim(TripletExtractor::$question_word) .
+                " " . trim($triplet['predicate']['RAW']) .
+                " " . trim($triplet['object']['RAW']);
+        }
+
+        if (isset($triplet['object']['FEATURED'])
+            && isset($triplet['predicate']['FEATURED'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['object']['FEATURED'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['predicate']['FEATURED'])
+        ) {
+            $generated_question_array['FEATURED']['1'] =
+                trim($triplet['object']['FEATURED']) .
+                " " . trim($triplet['predicate']['FEATURED']) .
+                " " . trim(TripletExtractor::$question_word);
+            $generated_question_array['FEATURED']['2'] =
+                trim(TripletExtractor::$question_word) .
+                " " . trim($triplet['predicate']['FEATURED']) .
+                " " . trim($triplet['object']['FEATURED']);
+        }
+        return $generated_question_array;
+    }
+
+    /**
+     * @param $question_string_tagged tagged question statement
+     * @param $index current index in statement
+     * @return array parsed triplet
+     */
+    public static function parseWHPlusQuestion_New($question_string_tagged, $index)
+    {
+        $generated_question_array = array();
+        $aux_verb = "";
+        while (isset($question_string_tagged[$index]) &&
+            ("VB" == trim($question_string_tagged[$index]['tag']) ||
+            "VBD" == trim($question_string_tagged[$index]['tag']) ||
+            "VBG" == trim($question_string_tagged[$index]['tag']) ||
+            "VBN" == trim($question_string_tagged[$index]['tag']) ||
+            "VBP" == trim($question_string_tagged[$index]['tag']) ||
+            "VBZ" == trim($question_string_tagged[$index]['tag']))) {
+            $aux_verb .= " " . trim($question_string_tagged[$index]['token']);
+            $index = $index + 1;
+        }
+
+        $tree = ["cur_node" => $index];
+        $tree['NP'] = "WHPlus";
+
+        $triplet = array();
+        $tree_np = TripletExtractor::extractNPUsingRDP($question_string_tagged, $tree);
+        $triplet['subject'] = TripletExtractor::extractSubjectFromTree($tree_np);
+        $tree_vp = TripletExtractor::extractVPUsingRDP($question_string_tagged, $tree);
+        $triplet['predicate'] = TripletExtractor::extractPredicateFromTree($tree_vp);
+        $triplet['object'] = TripletExtractor::extractObjectFromTree($tree_vp);
+
+        if (isset($aux_verb)
+            && !TripletExtractor::IsNullOrEmptyString($aux_verb)
+        ) {
+            $triplet['predicate']['RAW'] = trim($aux_verb) .
+                " " . $triplet['predicate']['RAW'];
+            if (!isset($triplet['predicate']['FEATURED'])) {
+                $triplet['predicate']['FEATURED'] = "";
+            }
+            $triplet['predicate']['FEATURED'] = trim($aux_verb) .
+                " " . $triplet['predicate']['FEATURED'];
+        }
+
+        if (isset($triplet['subject']['RAW'])
+            && isset($triplet['predicate']['RAW'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['subject']['RAW'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['predicate']['RAW'])
+        ) {
+            $generated_question_array['RAW']['1'] =
+                trim($triplet['subject']['RAW']) .
+                " " . trim($triplet['predicate']['RAW']) .
+                " " . trim(TripletExtractor::$question_word);
+            $generated_question_array['RAW']['2'] =
+                trim(TripletExtractor::$question_word) .
+                " " . trim($triplet['predicate']['RAW']) .
+                " " . trim($triplet['subject']['RAW']);
+        }
+
+        if (isset($triplet['subject']['FEATURED'])
+            && isset($triplet['predicate']['FEATURED'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['subject']['FEATURED'])
+            && !TripletExtractor::IsNullOrEmptyString($triplet['predicate']['FEATURED'])
+        ) {
+            $generated_question_array['FEATURED']['1'] =
+                trim($triplet['subject']['FEATURED']) .
+                " " . trim($triplet['predicate']['FEATURED']) .
+                " " . trim(TripletExtractor::$question_word);
+            $generated_question_array['FEATURED']['2'] =
+                trim(TripletExtractor::$question_word) .
+                " " . trim($triplet['predicate']['FEATURED']) .
+                " " . trim($triplet['subject']['FEATURED']);
+        }
+        return $generated_question_array;
+    }
+}
diff --git a/src/models/PhraseModel.php b/src/models/PhraseModel.php
index 8be310a..2e81fb0 100755
--- a/src/models/PhraseModel.php
+++ b/src/models/PhraseModel.php
@@ -38,6 +38,7 @@ use seekquarry\yioop\library\IndexManager;
 use seekquarry\yioop\library\PhraseParser;
 use seekquarry\yioop\library\Thesaurus;
 use seekquarry\yioop\library\index_bundle_iterators as I;
+use seekquarry\yioop\library\TripletExtractor;
 
 /**
  * logging is done during crawl not through web,
@@ -392,6 +393,16 @@ class PhraseModel extends ParallelModel
                         if (isset($out_results['PAGES'][$out_count])) {
                             $results['PAGES'][$i] =
                                 $out_results['PAGES'][$out_count];
+
+                            if (isset($out_results['PAGES'][$out_count][self::QUESTION_TRIPLETS])) {
+                                $triplets_with_answer = $out_results['PAGES'][$out_count][self::QUESTION_TRIPLETS];
+                                $question = trim($phrase);
+
+                                if (isset($triplets_with_answer[$question])) {
+                                    echo "<br/><font color='blue'><strong>Answer:</strong></font>";
+                                    Print "<font color='red'><strong>".$triplets_with_answer[$question]."</strong></font>";
+                                }
+                            }
                             $out_count++;
                         }
                     }
@@ -892,8 +903,34 @@ class PhraseModel extends ParallelModel
                 }
             }
         }
+        if ($this->isQuestion($phrase)) {
+            echo "<br/> Entered query is a Question:".$phrase."<br/>";
+            $generated_question = TripletExtractor::questionParser(trim($phrase));
+            echo "<br/> Generated questions from query:<br/>";
+            print_r($generated_question);
+            if(isset($generated_question['FEATURED'])){
+                $phrase = $generated_question['FEATURED']['1'];
+            }else if(isset($generated_question['RAW'])){
+                $phrase = $generated_question['RAW']['1'];
+            }
+        }
+        echo "<br/>Searching for question:".$phrase."<br/>";
         return $phrase;
     }
+
+    /**
+     * @param $phrase any statement
+     * @return bool returns true if statment is question
+     */
+    public function isQuestion($phrase)
+    {
+        $regex_starts_with_que = "/^(who|what|which|where|when|whose|whome|how)(.*)$/";
+        $regex_ends_with_que = "/^(.*)\?$/"; // Not in use
+        if (preg_match($regex_starts_with_que, trim($phrase))) {
+            return true;
+        }
+        return false;
+    }
     /**
      * Matches terms (non white-char strings) in the language $lang_tag in
      * $phrase that begin with  $start_with and don't contain  $not_contain,
diff --git a/src/views/elements/PageoptionsElement.php b/src/views/elements/PageoptionsElement.php
index 5b06f46..77c4970 100644
--- a/src/views/elements/PageoptionsElement.php
+++ b/src/views/elements/PageoptionsElement.php
@@ -403,6 +403,10 @@ class PageOptionsElement extends Element
                 e("<h3>".tl('pageoptions_element_extracted_words')."</h3>");
                 e("<pre>\n{$data['EXTRACTED_WORDS']}\n</pre>");
             }
+            if (isset($data["QUESTIONS_TRIPLET"])) {
+                e("<h3>" . tl('pageoptions_element_extracted_questions') . "</h3>");
+                e("<pre>\n{$data['QUESTIONS_TRIPLET']}\n</pre>");
+            }
             if (isset($data["EXTRACTED_META_WORDS"])) {
                 e("<h3>".tl('pageoptions_element_extracted_metas')."</h3>");
                 e("<pre>\n{$data['EXTRACTED_META_WORDS']}\n</pre>");
-- 
1.9.4.msysgit.2

