From aea07b0a1992bf2765031fa96d946c241de9a586 Mon Sep 17 00:00:00 2001 From: Dingyuan Wang Date: Fri, 3 Apr 2015 23:47:25 +0800 Subject: [PATCH 01/53] Fix some problems in EMS: * remove absolute links * fix coverage bar highlighting * change Base64 library to support UTF-8 --- scripts/ems/web/analysis.php | 8 +- scripts/ems/web/base64.js | 285 ++++++++++++------ scripts/ems/web/bilingual-concordance.css | 1 + scripts/ems/web/index.php | 2 +- .../scriptaculous-js-1.8.3/README.rdoc | 6 +- scripts/ems/web/overview.php | 5 +- 6 files changed, 196 insertions(+), 111 deletions(-) diff --git a/scripts/ems/web/analysis.php b/scripts/ems/web/analysis.php index a64d5977f..00bb9e15f 100644 --- a/scripts/ems/web/analysis.php +++ b/scripts/ems/web/analysis.php @@ -1261,8 +1261,8 @@ function input_annotation($sentence,$input,$segmentation,$filter) { for($j=$from;$j<=$to;$j++) { if ($j>$from) { $phrase .= " "; } $phrase .= $word[$j]; - $highlightwords .= " document.getElementById('inputword-$i-$j').style.backgroundColor='#ffff80';"; - $lowlightwords .= " document.getElementById('inputword-$i-$j').style.backgroundColor='".coverage_color($coverage[$j][$j])."';"; + $highlightwords .= " document.getElementById('inputword-$sentence-$j').style.backgroundColor='#ffff80';"; + $lowlightwords .= " document.getElementById('inputword-$sentence-$j').style.backgroundColor='".coverage_color($coverage[$j][$j])."';"; } print "
"; } @@ -1443,10 +1443,10 @@ function biconcor($query) { $sentence = $_GET['sentence']; $biconcor = get_biconcor_version($dir,$set,$id); print "
-
+ - +
"; $cmd = "./biconcor -html -l $dir/model/biconcor.$biconcor -Q ".base64_encode($query)." 2>/dev/null"; diff --git a/scripts/ems/web/base64.js b/scripts/ems/web/base64.js index e0e94d765..67fd9ad8d 100644 --- a/scripts/ems/web/base64.js +++ b/scripts/ems/web/base64.js @@ -1,108 +1,193 @@ -var END_OF_INPUT = -1; +/* + * $Id: base64.js,v 2.15 2014/04/05 12:58:57 dankogai Exp dankogai $ + * + * Licensed under the MIT license. + * http://opensource.org/licenses/mit-license + * + * References: + * http://en.wikipedia.org/wiki/Base64 + */ -var base64Chars = new Array( - 'A','B','C','D','E','F','G','H', - 'I','J','K','L','M','N','O','P', - 'Q','R','S','T','U','V','W','X', - 'Y','Z','a','b','c','d','e','f', - 'g','h','i','j','k','l','m','n', - 'o','p','q','r','s','t','u','v', - 'w','x','y','z','0','1','2','3', - '4','5','6','7','8','9','+','/' -); - -var reverseBase64Chars = new Array(); -for (var i=0; i < base64Chars.length; i++){ - reverseBase64Chars[base64Chars[i]] = i; -} - -var base64Str; -var base64Count; -function setBase64Str(str){ - base64Str = str; - base64Count = 0; -} -function readBase64(){ - if (!base64Str) return END_OF_INPUT; - if (base64Count >= base64Str.length) return END_OF_INPUT; - var c = base64Str.charCodeAt(base64Count) & 0xff; - base64Count++; - return c; -} -function encodeBase64(str){ - setBase64Str(str); - var result = ''; - var inBuffer = new Array(3); - var lineCount = 0; - var done = false; - while (!done && (inBuffer[0] = readBase64()) != END_OF_INPUT){ - inBuffer[1] = readBase64(); - inBuffer[2] = readBase64(); - result += (base64Chars[ inBuffer[0] >> 2 ]); - if (inBuffer[1] != END_OF_INPUT){ - result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30) | (inBuffer[1] >> 4) ]); - if (inBuffer[2] != END_OF_INPUT){ - result += (base64Chars [((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6) ]); - result += (base64Chars [inBuffer[2] & 0x3F]); - } else { - result += (base64Chars [((inBuffer[1] << 2) & 0x3c)]); - result += ('='); - done = true; - } +(function(global) { + 'use strict'; + // existing version for noConflict() + var _Base64 = global.Base64; + var version = "2.1.7"; + // if node.js, we use Buffer + var buffer; + if (typeof module !== 'undefined' && module.exports) { + buffer = require('buffer').Buffer; + } + // constants + var b64chars + = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + var b64tab = function(bin) { + var t = {}; + for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i; + return t; + }(b64chars); + var fromCharCode = String.fromCharCode; + // encoder stuff + var cb_utob = function(c) { + if (c.length < 2) { + var cc = c.charCodeAt(0); + return cc < 0x80 ? c + : cc < 0x800 ? (fromCharCode(0xc0 | (cc >>> 6)) + + fromCharCode(0x80 | (cc & 0x3f))) + : (fromCharCode(0xe0 | ((cc >>> 12) & 0x0f)) + + fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) + + fromCharCode(0x80 | ( cc & 0x3f))); } else { - result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30)]); - result += ('='); - result += ('='); - done = true; - } - lineCount += 4; - if (lineCount >= 76){ - result += ('\n'); - lineCount = 0; + var cc = 0x10000 + + (c.charCodeAt(0) - 0xD800) * 0x400 + + (c.charCodeAt(1) - 0xDC00); + return (fromCharCode(0xf0 | ((cc >>> 18) & 0x07)) + + fromCharCode(0x80 | ((cc >>> 12) & 0x3f)) + + fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) + + fromCharCode(0x80 | ( cc & 0x3f))); } + }; + var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g; + var utob = function(u) { + return u.replace(re_utob, cb_utob); + }; + var cb_encode = function(ccc) { + var padlen = [0, 2, 1][ccc.length % 3], + ord = ccc.charCodeAt(0) << 16 + | ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8) + | ((ccc.length > 2 ? ccc.charCodeAt(2) : 0)), + chars = [ + b64chars.charAt( ord >>> 18), + b64chars.charAt((ord >>> 12) & 63), + padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63), + padlen >= 1 ? '=' : b64chars.charAt(ord & 63) + ]; + return chars.join(''); + }; + var btoa = global.btoa ? function(b) { + return global.btoa(b); + } : function(b) { + return b.replace(/[\s\S]{1,3}/g, cb_encode); + }; + var _encode = buffer ? function (u) { + return (u.constructor === buffer.constructor ? u : new buffer(u)) + .toString('base64') } - return result; -} -function readReverseBase64(){ - if (!base64Str) return END_OF_INPUT; - while (true){ - if (base64Count >= base64Str.length) return END_OF_INPUT; - var nextCharacter = base64Str.charAt(base64Count); - base64Count++; - if (reverseBase64Chars[nextCharacter]){ - return reverseBase64Chars[nextCharacter]; + : function (u) { return btoa(utob(u)) } + ; + var encode = function(u, urisafe) { + return !urisafe + ? _encode(String(u)) + : _encode(String(u)).replace(/[+\/]/g, function(m0) { + return m0 == '+' ? '-' : '_'; + }).replace(/=/g, ''); + }; + var encodeURI = function(u) { return encode(u, true) }; + // decoder stuff + var re_btou = new RegExp([ + '[\xC0-\xDF][\x80-\xBF]', + '[\xE0-\xEF][\x80-\xBF]{2}', + '[\xF0-\xF7][\x80-\xBF]{3}' + ].join('|'), 'g'); + var cb_btou = function(cccc) { + switch(cccc.length) { + case 4: + var cp = ((0x07 & cccc.charCodeAt(0)) << 18) + | ((0x3f & cccc.charCodeAt(1)) << 12) + | ((0x3f & cccc.charCodeAt(2)) << 6) + | (0x3f & cccc.charCodeAt(3)), + offset = cp - 0x10000; + return (fromCharCode((offset >>> 10) + 0xD800) + + fromCharCode((offset & 0x3FF) + 0xDC00)); + case 3: + return fromCharCode( + ((0x0f & cccc.charCodeAt(0)) << 12) + | ((0x3f & cccc.charCodeAt(1)) << 6) + | (0x3f & cccc.charCodeAt(2)) + ); + default: + return fromCharCode( + ((0x1f & cccc.charCodeAt(0)) << 6) + | (0x3f & cccc.charCodeAt(1)) + ); } - if (nextCharacter == 'A') return 0; + }; + var btou = function(b) { + return b.replace(re_btou, cb_btou); + }; + var cb_decode = function(cccc) { + var len = cccc.length, + padlen = len % 4, + n = (len > 0 ? b64tab[cccc.charAt(0)] << 18 : 0) + | (len > 1 ? b64tab[cccc.charAt(1)] << 12 : 0) + | (len > 2 ? b64tab[cccc.charAt(2)] << 6 : 0) + | (len > 3 ? b64tab[cccc.charAt(3)] : 0), + chars = [ + fromCharCode( n >>> 16), + fromCharCode((n >>> 8) & 0xff), + fromCharCode( n & 0xff) + ]; + chars.length -= [0, 0, 2, 1][padlen]; + return chars.join(''); + }; + var atob = global.atob ? function(a) { + return global.atob(a); + } : function(a){ + return a.replace(/[\s\S]{1,4}/g, cb_decode); + }; + var _decode = buffer ? function(a) { + return (a.constructor === buffer.constructor + ? a : new buffer(a, 'base64')).toString(); } - return END_OF_INPUT; -} -function ntos(n){ - n=n.toString(16); - if (n.length == 1) n="0"+n; - n="%"+n; - return unescape(n); -} + : function(a) { return btou(atob(a)) }; + var decode = function(a){ + return _decode( + String(a).replace(/[-_]/g, function(m0) { return m0 == '-' ? '+' : '/' }) + .replace(/[^A-Za-z0-9\+\/]/g, '') + ); + }; + var noConflict = function() { + var Base64 = global.Base64; + global.Base64 = _Base64; + return Base64; + }; + // export Base64 + global.Base64 = { + VERSION: version, + atob: atob, + btoa: btoa, + fromBase64: decode, + toBase64: encode, + utob: utob, + encode: encode, + encodeURI: encodeURI, + btou: btou, + decode: decode, + noConflict: noConflict + }; + // if ES5 is available, make Base64.extendString() available + if (typeof Object.defineProperty === 'function') { + var noEnum = function(v){ + return {value:v,enumerable:false,writable:true,configurable:true}; + }; + global.Base64.extendString = function () { + Object.defineProperty( + String.prototype, 'fromBase64', noEnum(function () { + return decode(this) + })); + Object.defineProperty( + String.prototype, 'toBase64', noEnum(function (urisafe) { + return encode(this, urisafe) + })); + Object.defineProperty( + String.prototype, 'toBase64URI', noEnum(function () { + return encode(this, true) + })); + }; + } + // that's it! +})(this); -function decodeBase64(str){ - setBase64Str(str); - var result = ""; - var inBuffer = new Array(4); - var done = false; - while (!done && (inBuffer[0] = readReverseBase64()) != END_OF_INPUT - && (inBuffer[1] = readReverseBase64()) != END_OF_INPUT){ - inBuffer[2] = readReverseBase64(); - inBuffer[3] = readReverseBase64(); - result += ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4)); - if (inBuffer[2] != END_OF_INPUT){ - result += ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2)); - if (inBuffer[3] != END_OF_INPUT){ - result += ntos((((inBuffer[2] << 6) & 0xff) | inBuffer[3])); - } else { - done = true; - } - } else { - done = true; - } - } - return result; +if (this['Meteor']) { + Base64 = global.Base64; // for normal export in Meteor.js } diff --git a/scripts/ems/web/bilingual-concordance.css b/scripts/ems/web/bilingual-concordance.css index e232337d2..4648a21dd 100644 --- a/scripts/ems/web/bilingual-concordance.css +++ b/scripts/ems/web/bilingual-concordance.css @@ -93,5 +93,6 @@ span.mismatch_aligned { td.pp_more { font-size: 70%; + color: navy; text-align: center; } diff --git a/scripts/ems/web/index.php b/scripts/ems/web/index.php index 6b785cf3f..d216b114a 100644 --- a/scripts/ems/web/index.php +++ b/scripts/ems/web/index.php @@ -8,7 +8,7 @@ require("diff.php"); require("sgviz.php"); function head($title) { - print ' + print ' '.$title.' diff --git a/scripts/ems/web/javascripts/scriptaculous-js-1.8.3/README.rdoc b/scripts/ems/web/javascripts/scriptaculous-js-1.8.3/README.rdoc index 21f8c8cf6..57f78eb53 100644 --- a/scripts/ems/web/javascripts/scriptaculous-js-1.8.3/README.rdoc +++ b/scripts/ems/web/javascripts/scriptaculous-js-1.8.3/README.rdoc @@ -32,8 +32,8 @@ in a directory of your website, e.g. /javascripts. Now, you can include the scripts by adding the following tags to the HEAD section of your HTML pages: - - + + scriptaculous.js will automatically load the other files of the script.aculo.us distribution in, provided they are accessible @@ -56,4 +56,4 @@ the sources of the examples provided. == License script.aculo.us is licensed under the terms of the MIT License, -see the included MIT-LICENSE file. \ No newline at end of file +see the included MIT-LICENSE file. diff --git a/scripts/ems/web/overview.php b/scripts/ems/web/overview.php index e56ed6f08..ce0434bb8 100644 --- a/scripts/ems/web/overview.php +++ b/scripts/ems/web/overview.php @@ -1,6 +1,5 @@ $dir[0]$dir[1]$dir[2]$dir[3]\n"; } print "\n"; - print "

To add experiment, edit /fs/thor4/html/experiment/setup"; + print "

To add experiment, edit the \"setup\" file.

"; } function overview() { @@ -26,7 +25,7 @@ function overview() { head("Task: $task ($user)"); print "Wiki Notes"; - print "     |     Overview of experiments     |     $dir

"; + print "     |     Overview of experiments     |     $dir

"; reset($experiment); print "

\n"; From 54e55f2dcb65a70a52c7022553f8a6a4169fa54e Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Mon, 6 Apr 2015 11:31:44 +0400 Subject: [PATCH 02/53] better detection of pigz, sort, split. In case they are not in the default directory --- scripts/generic/extract-parallel.perl | 4 ++-- scripts/generic/score-parallel.perl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/generic/extract-parallel.perl b/scripts/generic/extract-parallel.perl index 687a21e28..3c7429212 100755 --- a/scripts/generic/extract-parallel.perl +++ b/scripts/generic/extract-parallel.perl @@ -32,8 +32,8 @@ my $glueFile; my $phraseOrientation = 0; my $phraseOrientationPriorsFile; -my $GZIP_EXEC; # = which("pigz"); -if(-f "/usr/bin/pigz") { +my $GZIP_EXEC; +if(`which pigz`) { $GZIP_EXEC = 'pigz'; } else { diff --git a/scripts/generic/score-parallel.perl b/scripts/generic/score-parallel.perl index d6f16b2fc..9979f087d 100755 --- a/scripts/generic/score-parallel.perl +++ b/scripts/generic/score-parallel.perl @@ -13,8 +13,8 @@ sub GetSourcePhrase($); sub NumStr($); sub CutContextFile($$$); -my $GZIP_EXEC; # = which("pigz"); -if(-f "/usr/bin/pigz") { +my $GZIP_EXEC; +if(`which pigz`) { $GZIP_EXEC = 'pigz'; } else { From 5817806ec764523bc27432b77c773bd134e1ef73 Mon Sep 17 00:00:00 2001 From: Flammie Pirinen Date: Tue, 7 Apr 2015 15:51:29 +0100 Subject: [PATCH 03/53] fix detokenising : in abbrev. case suffix case --- scripts/tokenizer/detokenizer.perl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/tokenizer/detokenizer.perl b/scripts/tokenizer/detokenizer.perl index 14d6666c9..5e7b3584e 100755 --- a/scripts/tokenizer/detokenizer.perl +++ b/scripts/tokenizer/detokenizer.perl @@ -176,6 +176,10 @@ sub detokenize { } + } elsif (($language eq "fi") && ($words[$i-1] =~ /:$/) && ($words[$i] =~ /^(N|n|ssa|ssä|a|ä|lla|llä|lta|ltä|lle|ksi|kse)(ni|si|mme|nne|nsa)?(ko|kö|han|hän|pa|pä|kaan|kään|kin)?$/)) { + # Finnish : without intervening space if followed by case suffix + $text=$text.$words[$i]; + $prependSpace = " "; } else { $text=$text.$prependSpace.$words[$i]; $prependSpace = " "; From f9deb6de3be4a566fa5cf52bc9db5fe02c376b4f Mon Sep 17 00:00:00 2001 From: Flammie Pirinen Date: Tue, 7 Apr 2015 15:55:02 +0100 Subject: [PATCH 04/53] also lowercase if case fail --- scripts/tokenizer/detokenizer.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tokenizer/detokenizer.perl b/scripts/tokenizer/detokenizer.perl index 5e7b3584e..d759568e4 100755 --- a/scripts/tokenizer/detokenizer.perl +++ b/scripts/tokenizer/detokenizer.perl @@ -178,7 +178,7 @@ sub detokenize { } elsif (($language eq "fi") && ($words[$i-1] =~ /:$/) && ($words[$i] =~ /^(N|n|ssa|ssä|a|ä|lla|llä|lta|ltä|lle|ksi|kse)(ni|si|mme|nne|nsa)?(ko|kö|han|hän|pa|pä|kaan|kään|kin)?$/)) { # Finnish : without intervening space if followed by case suffix - $text=$text.$words[$i]; + $text=$text. lc $words[$i]; $prependSpace = " "; } else { $text=$text.$prependSpace.$words[$i]; From 85230e8334573df89c7ceba2ec7436ef2d7ee49f Mon Sep 17 00:00:00 2001 From: Flammie Pirinen Date: Tue, 7 Apr 2015 16:00:48 +0100 Subject: [PATCH 05/53] add fi to list to silence warnings --- scripts/tokenizer/detokenizer.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tokenizer/detokenizer.perl b/scripts/tokenizer/detokenizer.perl index d759568e4..2af94a1e0 100755 --- a/scripts/tokenizer/detokenizer.perl +++ b/scripts/tokenizer/detokenizer.perl @@ -36,7 +36,7 @@ if ($HELP) { exit; } -if ($language !~ /^(cs|en|fr|it)$/) { +if ($language !~ /^(cs|en|fr|it|fi)$/) { print STDERR "Warning: No built-in rules for language $language.\n" } From ef52bc66f604a59c1c637d1c59b277df0bfbbbaa Mon Sep 17 00:00:00 2001 From: Flammie Pirinen Date: Tue, 7 Apr 2015 16:16:43 +0100 Subject: [PATCH 06/53] full set of cases and caps --- scripts/tokenizer/detokenizer.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tokenizer/detokenizer.perl b/scripts/tokenizer/detokenizer.perl index 2af94a1e0..908158da6 100755 --- a/scripts/tokenizer/detokenizer.perl +++ b/scripts/tokenizer/detokenizer.perl @@ -176,7 +176,7 @@ sub detokenize { } - } elsif (($language eq "fi") && ($words[$i-1] =~ /:$/) && ($words[$i] =~ /^(N|n|ssa|ssä|a|ä|lla|llä|lta|ltä|lle|ksi|kse)(ni|si|mme|nne|nsa)?(ko|kö|han|hän|pa|pä|kaan|kään|kin)?$/)) { + } elsif (($language eq "fi") && ($words[$i-1] =~ /:$/) && ($words[$i] =~ /^(N|n|A|a|Ä|ä|ssa|Ssa|ssä|Ssä|sta|stä|Sta|Stä|hun|Hun|hyn|Hyn|han|Han|hän|Hän|hön|Hön|un|Un|yn|Yn|an|An|än|Än|ön|Ön|seen|Seen|lla|Lla|llä|Llä|lta|Lta|ltä|Ltä|lle|Lle|ksi|Ksi|kse|Kse|tta|Tta|ine|Ine)(ni|si|mme|nne|nsa)?(ko|kö|han|hän|pa|pä|kaan|kään|kin)?$/)) { # Finnish : without intervening space if followed by case suffix $text=$text. lc $words[$i]; $prependSpace = " "; From fc8ee03b8d51e7c2533dd685042b75c32528d380 Mon Sep 17 00:00:00 2001 From: Flammie Pirinen Date: Tue, 7 Apr 2015 16:19:07 +0100 Subject: [PATCH 07/53] examples --- scripts/tokenizer/detokenizer.perl | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/tokenizer/detokenizer.perl b/scripts/tokenizer/detokenizer.perl index 908158da6..27e315840 100755 --- a/scripts/tokenizer/detokenizer.perl +++ b/scripts/tokenizer/detokenizer.perl @@ -178,6 +178,7 @@ sub detokenize { } elsif (($language eq "fi") && ($words[$i-1] =~ /:$/) && ($words[$i] =~ /^(N|n|A|a|Ä|ä|ssa|Ssa|ssä|Ssä|sta|stä|Sta|Stä|hun|Hun|hyn|Hyn|han|Han|hän|Hän|hön|Hön|un|Un|yn|Yn|an|An|än|Än|ön|Ön|seen|Seen|lla|Lla|llä|Llä|lta|Lta|ltä|Ltä|lle|Lle|ksi|Ksi|kse|Kse|tta|Tta|ine|Ine)(ni|si|mme|nne|nsa)?(ko|kö|han|hän|pa|pä|kaan|kään|kin)?$/)) { # Finnish : without intervening space if followed by case suffix + # EU:N EU:n EU:ssa EU:sta EU:hun EU:iin ... $text=$text. lc $words[$i]; $prependSpace = " "; } else { From 464615a0c3fc62bf3a5e79e9f09034ddf3888a54 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Tue, 7 Apr 2015 22:58:17 +0700 Subject: [PATCH 08/53] Fix some clang++ warnings. Compiling with clang++ at the default warning/error levels produces some interesting warnings. Here's a pair of fixes for the simplest instances: moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp:133:7: warning: comparison of array 'path' equal to a null pointer is always false [-Wtautological-pointer-compare] if (path == NULL) { ^~~~ ~~~~ (The code unnecessarily checks that an automatic variable has a non-null address). moses/TranslationModel/DynSAInclude/onlineRLM.h:305:20: warning: unsequenced modification and access to 'den_val' [-Wunsequenced] if(((den_val = query(&ngram[len - num_fnd], num_fnd - 1)) > 0) && ^ (The code tries to cram too much into an "if" condition.) --- moses/TranslationModel/DynSAInclude/onlineRLM.h | 3 ++- .../TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/moses/TranslationModel/DynSAInclude/onlineRLM.h b/moses/TranslationModel/DynSAInclude/onlineRLM.h index 1d3f66eac..050e016c9 100644 --- a/moses/TranslationModel/DynSAInclude/onlineRLM.h +++ b/moses/TranslationModel/DynSAInclude/onlineRLM.h @@ -302,7 +302,8 @@ float OnlineRLM::getProb(const wordID_t* ngram, int len, } while(num_fnd > 1) { // get lower order count //get sub-context of size one less than length found (exluding target) - if(((den_val = query(&ngram[len - num_fnd], num_fnd - 1)) > 0) && + den_val = query(&ngram[len - num_fnd], num_fnd - 1); + if((den_val > 0) && (den_val >= in[len - num_fnd]) && (in[len - num_fnd] > 0)) { break; } else --num_fnd; // else backoff to lower ngram order diff --git a/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp b/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp index 1ca9dce67..dc25b805b 100644 --- a/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp +++ b/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp @@ -130,10 +130,6 @@ int removedirectoryrecursively(const char *dirname) struct dirent *entry; char path[PATH_MAX]; - if (path == NULL) { - fprintf(stderr, "Out of memory error\n"); - return 0; - } dir = opendir(dirname); if (dir == NULL) { perror("Error opendir()"); From 2682cc0f9b43824c9ba47f1fc2e41a7ae5845da5 Mon Sep 17 00:00:00 2001 From: Michael Denkowski Date: Tue, 7 Apr 2015 17:06:18 -0400 Subject: [PATCH 09/53] typo fix --- scripts/training/bilingual-lm/extract_training.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/training/bilingual-lm/extract_training.py b/scripts/training/bilingual-lm/extract_training.py index 66f8f0413..cd8755580 100755 --- a/scripts/training/bilingual-lm/extract_training.py +++ b/scripts/training/bilingual-lm/extract_training.py @@ -147,7 +147,7 @@ def main(): #Numberize the file for line in ngrams_file_handle: - numberized_file_handle.write(extract.numberize(line, m, n, tvocab_idmap, tvocab_idmap)) + numberized_file_handle.write(extract.numberize(line, options.m, options.n, svocab_idmap, tvocab_idmap)) numberized_file_handle.close() ngrams_file_handle.close() From 0698da8b0fd54825bdafbfffdd66f78ca2857927 Mon Sep 17 00:00:00 2001 From: Kenneth Heafield Date: Wed, 8 Apr 2015 10:08:05 -0400 Subject: [PATCH 10/53] log(1 + ...) -> log1p(...) --- moses/Util.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/moses/Util.h b/moses/Util.h index 48e6a51ae..68989721c 100644 --- a/moses/Util.h +++ b/moses/Util.h @@ -502,13 +502,11 @@ inline std::string GetFirstString(const std::string& str, int& first_pos, const template T log_sum (T log_a, T log_b) { - T v; if (log_a < log_b) { - v = log_b+log ( 1 + exp ( log_a-log_b )); + return log_b + log1p(exp(log_a - log_b)); } else { - v = log_a+log ( 1 + exp ( log_b-log_a )); + return log_a + log1p(exp(log_b - log_a)); } - return ( v ); } /** From 8a3ae2fd5c4fc11fafe2ba64cf29a655688f043a Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Fri, 10 Apr 2015 12:54:34 +0700 Subject: [PATCH 11/53] Portability and include fixes. Add include for srand()/rand(), and for open() etc. Include on Windows if using MinGW. Disable MeteorScorer on Windows, since it doesn't have fork() and pipe(). --- mert/Fdstream.h | 2 ++ mert/MeteorScorer.cpp | 3 ++- mert/evaluator.cpp | 1 + util/unistd.hh | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mert/Fdstream.h b/mert/Fdstream.h index 2258ef4a5..23eecc466 100644 --- a/mert/Fdstream.h +++ b/mert/Fdstream.h @@ -13,6 +13,8 @@ #include #include +#include "util/unistd.hh" + #if defined(__GLIBCXX__) || defined(__GLIBCPP__) #include diff --git a/mert/MeteorScorer.cpp b/mert/MeteorScorer.cpp index 1254ec95f..f4c7997ee 100644 --- a/mert/MeteorScorer.cpp +++ b/mert/MeteorScorer.cpp @@ -18,6 +18,7 @@ #include "ScoreStats.h" #include "Util.h" +#include "util/unistd.hh" using namespace std; @@ -25,7 +26,7 @@ namespace MosesTuning { // Meteor supported -#if defined(__GLIBCXX__) || defined(__GLIBCPP__) +#if (defined(__GLIBCXX__) || defined(__GLIBCPP__)) && !defined(_WIN32) // for clarity #define CHILD_STDIN_READ pipefds_input[0] diff --git a/mert/evaluator.cpp b/mert/evaluator.cpp index 25da9adbc..4e791fc6e 100644 --- a/mert/evaluator.cpp +++ b/mert/evaluator.cpp @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/util/unistd.hh b/util/unistd.hh index 0379c4914..f99be592a 100644 --- a/util/unistd.hh +++ b/util/unistd.hh @@ -1,7 +1,7 @@ #ifndef UTIL_UNISTD_H #define UTIL_UNISTD_H -#if defined(_WIN32) || defined(_WIN64) +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) // Windows doesn't define // From b8793fb78871f90b2626b55455ab3a804a49330a Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Fri, 10 Apr 2015 13:25:51 +0700 Subject: [PATCH 12/53] Address two TODO notes in mert/evaluator.cpp. The notes were about two objects which were created on the free store using "new", then cleaned up using "delete". May have been a Java habit; the solution was as simple as creating them on the stack. --- mert/evaluator.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/mert/evaluator.cpp b/mert/evaluator.cpp index 4e791fc6e..026abf397 100644 --- a/mert/evaluator.cpp +++ b/mert/evaluator.cpp @@ -92,17 +92,15 @@ void EvaluatorUtil::evaluate(const string& candFile, int bootstrap, bool nbest_i if (bootstrap) { vector scores; for (int i = 0; i < bootstrap; ++i) { - // TODO: Use smart pointer for exceptional-safety. - ScoreData* scoredata = new ScoreData(g_scorer); + ScoreData scoredata(g_scorer); for (int j = 0; j < n; ++j) { int randomIndex = random() % n; - scoredata->add(entries[randomIndex], j); + scoredata.add(entries[randomIndex], j); } - g_scorer->setScoreData(scoredata); + g_scorer->setScoreData(&scoredata); candidates_t candidates(n, 0); float score = g_scorer->score(candidates); scores.push_back(score); - delete scoredata; } float avg = average(scores); @@ -122,15 +120,13 @@ void EvaluatorUtil::evaluate(const string& candFile, int bootstrap, bool nbest_i cout.precision(4); cout << avg << "\t[" << lb << "," << rb << "]" << endl; } else { - // TODO: Use smart pointer for exceptional-safety. - ScoreData* scoredata = new ScoreData(g_scorer); + ScoreData scoredata(g_scorer); for (int sid = 0; sid < n; ++sid) { - scoredata->add(entries[sid], sid); + scoredata.add(entries[sid], sid); } - g_scorer->setScoreData(scoredata); + g_scorer->setScoreData(&scoredata); candidates_t candidates(n, 0); float score = g_scorer->score(candidates); - delete scoredata; if (g_has_more_files) cout << candFile << "\t"; if (g_has_more_scorers) cout << g_scorer->getName() << "\t"; From d6a66d39bd3631401c5977ae508e5c7a44e65359 Mon Sep 17 00:00:00 2001 From: Kenneth Heafield Date: Fri, 10 Apr 2015 09:36:57 -0400 Subject: [PATCH 13/53] Delete unused code --- mert/FileStream.cpp | 25 ------------------------- mert/FileStream.h | 16 ---------------- 2 files changed, 41 deletions(-) diff --git a/mert/FileStream.cpp b/mert/FileStream.cpp index 800ce1bfe..3d908de4f 100644 --- a/mert/FileStream.cpp +++ b/mert/FileStream.cpp @@ -40,28 +40,3 @@ inputfilestream::~inputfilestream() void inputfilestream::close() { } - -outputfilestream::outputfilestream(const std::string &filePath) - : std::ostream(0), m_streambuf(0), m_is_good(false) -{ - // check if file is readable - std::filebuf* fb = new std::filebuf(); - m_is_good = (fb->open(filePath.c_str(), std::ios::out) != NULL); - - if (IsGzipFile(filePath)) { - throw runtime_error("Output to a zipped file not supported!"); - } else { - m_streambuf = fb; - } - this->init(m_streambuf); -} - -outputfilestream::~outputfilestream() -{ - delete m_streambuf; - m_streambuf = 0; -} - -void outputfilestream::close() -{ -} diff --git a/mert/FileStream.h b/mert/FileStream.h index 582cbcb59..8cbf4f591 100644 --- a/mert/FileStream.h +++ b/mert/FileStream.h @@ -22,20 +22,4 @@ public: void close(); }; -class outputfilestream : public std::ostream -{ -protected: - std::streambuf *m_streambuf; - bool m_is_good; - -public: - explicit outputfilestream(const std::string &filePath); - virtual ~outputfilestream(); - - bool good() const { - return m_is_good; - } - void close(); -}; - #endif // MERT_FILE_STREAM_H_ From 2f7c328db9d17be2076ccc5b6ed59a7c612575ac Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Sat, 11 Apr 2015 20:21:50 +0400 Subject: [PATCH 14/53] codelite --- contrib/other-builds/all.workspace | 4 ++-- .../manual-label/manual-label.project | 23 +++++++++++++++++-- contrib/other-builds/moses/moses.project | 2 -- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/contrib/other-builds/all.workspace b/contrib/other-builds/all.workspace index 787fda633..7b1d7862b 100644 --- a/contrib/other-builds/all.workspace +++ b/contrib/other-builds/all.workspace @@ -7,8 +7,8 @@ - - + + diff --git a/contrib/other-builds/manual-label/manual-label.project b/contrib/other-builds/manual-label/manual-label.project index 2bc69a6ca..3e3efcddb 100644 --- a/contrib/other-builds/manual-label/manual-label.project +++ b/contrib/other-builds/manual-label/manual-label.project @@ -1,5 +1,22 @@ + + + + + + + + @@ -14,6 +31,8 @@ + + @@ -33,6 +52,8 @@ + + @@ -107,6 +128,4 @@ - - diff --git a/contrib/other-builds/moses/moses.project b/contrib/other-builds/moses/moses.project index 7d666558f..55bf4e8f1 100644 --- a/contrib/other-builds/moses/moses.project +++ b/contrib/other-builds/moses/moses.project @@ -474,8 +474,6 @@ - - From 05b31b53f22abfabb8141b6cd7b0246890d521a1 Mon Sep 17 00:00:00 2001 From: Phil Williams Date: Mon, 13 Apr 2015 16:31:58 +0100 Subject: [PATCH 15/53] Implement -output-unknowns for search algorithms 7 and 9 (T2S/F2S) --- moses/Syntax/F2S/HyperTreeLoader.cpp | 24 +++++++++++++++++++++--- moses/Syntax/F2S/HyperTreeLoader.h | 10 +++++++++- moses/Syntax/F2S/Manager-inl.h | 24 +++++++++++++++++++++++- moses/Syntax/F2S/Manager.h | 3 +++ moses/Syntax/RuleTableFF.cpp | 3 ++- moses/Syntax/RuleTableFF.h | 7 +++++++ 6 files changed, 65 insertions(+), 6 deletions(-) diff --git a/moses/Syntax/F2S/HyperTreeLoader.cpp b/moses/Syntax/F2S/HyperTreeLoader.cpp index f3caa2cec..bd19cbace 100644 --- a/moses/Syntax/F2S/HyperTreeLoader.cpp +++ b/moses/Syntax/F2S/HyperTreeLoader.cpp @@ -40,12 +40,12 @@ bool HyperTreeLoader::Load(const std::vector &input, const std::vector &output, const std::string &inFile, const RuleTableFF &ff, - HyperTree &trie) + HyperTree &trie, + boost::unordered_set &sourceTermSet) { PrintUserTime(std::string("Start loading HyperTree")); - // const StaticData &staticData = StaticData::Instance(); - // const std::string &factorDelimiter = staticData.GetFactorDelimiter(); + sourceTermSet.clear(); std::size_t count = 0; @@ -106,6 +106,7 @@ bool HyperTreeLoader::Load(const std::vector &input, // Source-side HyperPath sourceFragment; hyperPathLoader.Load(sourceString, sourceFragment); + ExtractSourceTerminalSetFromHyperPath(sourceFragment, sourceTermSet); // Target-side TargetPhrase *targetPhrase = new TargetPhrase(&ff); @@ -144,6 +145,23 @@ bool HyperTreeLoader::Load(const std::vector &input, return true; } +void HyperTreeLoader::ExtractSourceTerminalSetFromHyperPath( + const HyperPath &hp, boost::unordered_set &sourceTerminalSet) +{ + for (std::vector::const_iterator p = hp.nodeSeqs.begin(); + p != hp.nodeSeqs.end(); ++p) { + for (std::vector::const_iterator q = p->begin(); + q != p->end(); ++q) { + const std::size_t factorId = *q; + if (factorId >= moses_MaxNumNonterminals && + factorId != HyperPath::kComma && + factorId != HyperPath::kEpsilon) { + sourceTerminalSet.insert(factorId); + } + } + } +} + } // namespace F2S } // namespace Syntax } // namespace Moses diff --git a/moses/Syntax/F2S/HyperTreeLoader.h b/moses/Syntax/F2S/HyperTreeLoader.h index ea009022d..088c7eaf5 100644 --- a/moses/Syntax/F2S/HyperTreeLoader.h +++ b/moses/Syntax/F2S/HyperTreeLoader.h @@ -3,9 +3,12 @@ #include #include +#include + #include "moses/TypeDef.h" #include "moses/Syntax/RuleTableFF.h" +#include "HyperPath.h" #include "HyperTree.h" #include "HyperTreeCreator.h" @@ -23,7 +26,12 @@ public: const std::vector &output, const std::string &inFile, const RuleTableFF &, - HyperTree &); + HyperTree &, + boost::unordered_set &); + +private: + void ExtractSourceTerminalSetFromHyperPath( + const HyperPath &, boost::unordered_set &); }; } // namespace F2S diff --git a/moses/Syntax/F2S/Manager-inl.h b/moses/Syntax/F2S/Manager-inl.h index a422e8085..f7f8f0ae9 100644 --- a/moses/Syntax/F2S/Manager-inl.h +++ b/moses/Syntax/F2S/Manager-inl.h @@ -38,6 +38,7 @@ Manager::Manager(const InputType &source) if (const ForestInput *p = dynamic_cast(&source)) { m_forest = p->GetForest(); m_rootVertex = p->GetRootVertex(); + m_sentenceLength = p->GetSize(); } else if (const TreeInput *p = dynamic_cast(&source)) { T2S::InputTreeBuilder builder; T2S::InputTree tmpTree; @@ -45,6 +46,7 @@ Manager::Manager(const InputType &source) boost::shared_ptr forest = boost::make_shared(); m_rootVertex = T2S::InputTreeToForest(tmpTree, *forest); m_forest = forest; + m_sentenceLength = p->GetSize(); } else { UTIL_THROW2("ERROR: F2S::Manager requires input to be a tree or forest"); } @@ -82,8 +84,13 @@ void Manager::Decode() p = sortedVertices.begin(); p != sortedVertices.end(); ++p) { const Forest::Vertex &vertex = **p; - // Skip terminal vertices. + // Skip terminal vertices (after checking if they are OOVs). if (vertex.incoming.empty()) { + if (vertex.pvertex.span.GetStartPos() > 0 && + vertex.pvertex.span.GetEndPos() < m_sentenceLength-1 && + IsUnknownSourceWord(vertex.pvertex.symbol)) { + m_oovs.insert(vertex.pvertex.symbol); + } continue; } @@ -189,6 +196,21 @@ void Manager::InitializeStacks() } } +template +bool Manager::IsUnknownSourceWord(const Word &w) const +{ + const std::size_t factorId = w[0]->GetId(); + const std::vector &ffs = RuleTableFF::Instances(); + for (std::size_t i = 0; i < ffs.size(); ++i) { + RuleTableFF *ff = ffs[i]; + const boost::unordered_set &sourceTerms = + ff->GetSourceTerminalSet(); + if (sourceTerms.find(factorId) != sourceTerms.end()) { + return false; + } + } + return true; +} template const SHyperedge *Manager::GetBestSHyperedge() const diff --git a/moses/Syntax/F2S/Manager.h b/moses/Syntax/F2S/Manager.h index 3c7ff8da1..90f34c04b 100644 --- a/moses/Syntax/F2S/Manager.h +++ b/moses/Syntax/F2S/Manager.h @@ -51,10 +51,13 @@ private: void InitializeStacks(); + bool IsUnknownSourceWord(const Word &) const; + void RecombineAndSort(const std::vector &, SVertexStack &); boost::shared_ptr m_forest; const Forest::Vertex *m_rootVertex; + std::size_t m_sentenceLength; // Includes and PVertexToStackMap m_stackMap; boost::shared_ptr m_glueRuleTrie; std::vector > m_mainRuleMatchers; diff --git a/moses/Syntax/RuleTableFF.cpp b/moses/Syntax/RuleTableFF.cpp index f4e06f489..37063e048 100644 --- a/moses/Syntax/RuleTableFF.cpp +++ b/moses/Syntax/RuleTableFF.cpp @@ -35,7 +35,8 @@ void RuleTableFF::Load() staticData.GetSearchAlgorithm() == SyntaxT2S) { F2S::HyperTree *trie = new F2S::HyperTree(this); F2S::HyperTreeLoader loader; - loader.Load(m_input, m_output, m_filePath, *this, *trie); + loader.Load(m_input, m_output, m_filePath, *this, *trie, + m_sourceTerminalSet); m_table = trie; } else if (staticData.GetSearchAlgorithm() == SyntaxS2T) { S2TParsingAlgorithm algorithm = staticData.GetS2TParsingAlgorithm(); diff --git a/moses/Syntax/RuleTableFF.h b/moses/Syntax/RuleTableFF.h index 4d6132e86..25e7d8428 100644 --- a/moses/Syntax/RuleTableFF.h +++ b/moses/Syntax/RuleTableFF.h @@ -43,10 +43,17 @@ public: return 0; } + // Get the source terminal vocabulary for this table's grammar (as a set of + // factor IDs) + const boost::unordered_set &GetSourceTerminalSet() const { + return m_sourceTerminalSet; + } + private: static std::vector s_instances; const RuleTable *m_table; + boost::unordered_set m_sourceTerminalSet; }; } // Syntax From 6162223690d8a9a3d461a85acdafe9cbef7bf3e5 Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Mon, 13 Apr 2015 20:42:33 +0400 Subject: [PATCH 16/53] add use warnings to all perl scripts --- scripts/OSM/OSM-Train.perl | 1 + scripts/OSM/extract-singletons.perl | 1 + scripts/OSM/flipAlignment.perl | 4 +++- scripts/Transliteration/clean.pl | 1 + scripts/Transliteration/corpusCreator.pl | 1 + scripts/Transliteration/in-decoding-transliteration.pl | 1 + scripts/Transliteration/post-decoding-transliteration.pl | 1 + .../Transliteration/prepare-transliteration-phrase-table.pl | 1 + scripts/Transliteration/threshold.pl | 1 + scripts/Transliteration/train-transliteration-module.pl | 1 + .../analysis/bootstrap-hypothesis-difference-significance.pl | 1 + scripts/analysis/sentence-by-sentence.pl | 1 + scripts/analysis/sg2dot.perl | 1 + scripts/analysis/show-phrases-used.pl | 2 ++ scripts/analysis/smtgui/filter-phrase-table.pl | 1 + scripts/ems/experiment.perl | 1 + scripts/ems/fix-info.perl | 1 + scripts/ems/support/analysis.perl | 1 + scripts/ems/support/build-domain-file-from-subcorpora.perl | 1 + scripts/ems/support/build-sparse-features.perl | 1 + scripts/ems/support/consolidate-training-data.perl | 1 + scripts/ems/support/generic-multicore-parallelizer.perl | 1 + scripts/ems/support/generic-parallelizer.perl | 1 + scripts/ems/support/input-from-sgm.perl | 1 + scripts/ems/support/interpolate-lm.perl | 1 + scripts/ems/support/lmplz-wrapper.perl | 1 + scripts/ems/support/mml-filter.perl | 1 + scripts/ems/support/mml-score.perl | 1 + scripts/ems/support/mml-train.perl | 1 + scripts/ems/support/prepare-fast-align.perl | 1 + scripts/ems/support/reference-from-sgm.perl | 1 + scripts/ems/support/remove-segmentation-markup.perl | 1 + scripts/ems/support/report-experiment-scores.perl | 1 + scripts/ems/support/run-command-on-multiple-refsets.perl | 1 + scripts/ems/support/run-wade.perl | 1 + scripts/ems/support/split-sentences.perl | 1 + scripts/ems/support/submit-grid.perl | 1 + .../ems/support/substitute-filtered-tables-and-weights.perl | 1 + scripts/ems/support/substitute-filtered-tables.perl | 2 ++ scripts/ems/support/substitute-weights.perl | 2 ++ scripts/ems/support/symmetrize-fast-align.perl | 1 + scripts/ems/support/thot-lm-wrapper.perl | 1 + scripts/ems/support/wrap-xml.perl | 1 + scripts/ems/web/progress.perl | 1 + scripts/fuzzy-match/create_xml.perl | 1 + scripts/generic/compound-splitter.perl | 1 + scripts/generic/extract-factors.pl | 1 + scripts/generic/extract-parallel.perl | 1 + scripts/generic/fsa2fsal.pl | 1 + scripts/generic/fsa2plf.pl | 1 + scripts/generic/fsal2fsa.pl | 1 + scripts/generic/generic-parallel.perl | 1 + scripts/generic/giza-parallel.perl | 1 + scripts/generic/lopar2pos.pl | 2 ++ scripts/generic/moses-parallel.pl | 1 + scripts/generic/mteval-v12.pl | 1 + scripts/generic/multi-bleu.perl | 1 + scripts/generic/ph_numbers.perl | 1 + scripts/generic/qsub-wrapper.pl | 1 + scripts/generic/reverse-alignment.perl | 1 + scripts/generic/score-parallel.perl | 1 + scripts/generic/strip-xml.perl | 1 + scripts/generic/trainlm-irst2.perl | 1 + scripts/generic/trainlm-lmplz.perl | 1 + scripts/other/beautify.perl | 1 + scripts/other/delete-scores.perl | 1 + scripts/other/get_many_translations_from_google.perl | 1 + scripts/recaser/detruecase.perl | 1 + scripts/recaser/recase.perl | 1 + scripts/recaser/train-recaser.perl | 1 + scripts/recaser/train-truecaser.perl | 1 + scripts/recaser/truecase.perl | 2 ++ scripts/regression-testing/compare-results.pl | 1 + scripts/regression-testing/create_localized_moses_ini.pl | 1 + scripts/regression-testing/modify-pars.pl | 1 + scripts/regression-testing/moses-virtual.pl | 1 + scripts/regression-testing/run-single-test.pl | 1 + scripts/regression-testing/run-test-suite.pl | 1 + scripts/tokenizer/deescape-special-chars-PTB.perl | 1 + scripts/tokenizer/deescape-special-chars.perl | 1 + scripts/tokenizer/detokenizer.perl | 2 ++ scripts/tokenizer/escape-special-chars.perl | 1 + scripts/tokenizer/lowercase.perl | 1 + scripts/tokenizer/normalize-punctuation.perl | 1 + scripts/tokenizer/pre-tokenizer.perl | 1 + scripts/tokenizer/remove-non-printing-char.perl | 1 + scripts/tokenizer/replace-unicode-punctuation.perl | 1 + scripts/tokenizer/tokenizer.perl | 1 + scripts/tokenizer/tokenizer_PTB.perl | 1 + scripts/training/absolutize_moses_model.pl | 2 ++ scripts/training/binarize-model.perl | 1 + scripts/training/build-generation-table.perl | 1 + scripts/training/build-mmsapt.perl | 1 + scripts/training/clean-corpus-n.perl | 1 + scripts/training/clone_moses_model.pl | 1 + scripts/training/convert-moses-ini-to-v2.perl | 1 + scripts/training/corpus-sizes.perl | 1 + scripts/training/exodus.perl | 1 + scripts/training/filter-model-given-input.pl | 1 + scripts/training/get-lexical.perl | 1 + scripts/training/giza2bal.pl | 2 ++ scripts/training/mert-moses.pl | 1 + scripts/training/postprocess-lopar.perl | 1 + scripts/training/reduce-factors.perl | 1 + .../remove-orphan-phrase-pairs-from-reordering-table.perl | 1 + scripts/training/threshold-filter.perl | 1 + scripts/training/train-global-lexicon-model.perl | 1 + scripts/training/train-model.perl | 1 + scripts/training/wrappers/berkeleyparsed2mosesxml.perl | 1 + scripts/training/wrappers/berkeleyparsed2mosesxml_PTB.perl | 1 + scripts/training/wrappers/filter-excluded-lines.perl | 1 + scripts/training/wrappers/find-unparseable.perl | 1 + scripts/training/wrappers/mada-wrapper.perl | 1 + .../training/wrappers/make-factor-brown-cluster-mkcls.perl | 1 + scripts/training/wrappers/make-factor-de-morph.perl | 1 + scripts/training/wrappers/make-factor-de-pos.perl | 1 + scripts/training/wrappers/make-factor-en-pos.mxpost.perl | 1 + scripts/training/wrappers/make-factor-pos.tree-tagger.perl | 1 + scripts/training/wrappers/make-factor-stem.perl | 1 + scripts/training/wrappers/make-factor-suffix.perl | 1 + scripts/training/wrappers/mosesxml2berkeleyparsed.perl | 1 + scripts/training/wrappers/parse-de-berkeley.perl | 1 + scripts/training/wrappers/parse-de-bitpar.perl | 1 + scripts/training/wrappers/parse-en-collins.perl | 1 + scripts/training/wrappers/parse-en-egret.perl | 1 + scripts/training/wrappers/syntax-hyphen-splitting.perl | 1 + scripts/training/wrappers/tagger-german-chunk.perl | 1 + 127 files changed, 137 insertions(+), 1 deletion(-) diff --git a/scripts/OSM/OSM-Train.perl b/scripts/OSM/OSM-Train.perl index 27ecfe342..e2b604f0b 100755 --- a/scripts/OSM/OSM-Train.perl +++ b/scripts/OSM/OSM-Train.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; use FindBin qw($RealBin); diff --git a/scripts/OSM/extract-singletons.perl b/scripts/OSM/extract-singletons.perl index faa4e8dd6..83719502f 100755 --- a/scripts/OSM/extract-singletons.perl +++ b/scripts/OSM/extract-singletons.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use Getopt::Std; getopts('q'); diff --git a/scripts/OSM/flipAlignment.perl b/scripts/OSM/flipAlignment.perl index 3c14a4542..3559bf79b 100755 --- a/scripts/OSM/flipAlignment.perl +++ b/scripts/OSM/flipAlignment.perl @@ -1,5 +1,7 @@ #!/usr/bin/env perl - use strict; + +use warnings; +use strict; my $file = shift(@ARGV); open(MYFILE, $file); diff --git a/scripts/Transliteration/clean.pl b/scripts/Transliteration/clean.pl index 252a25075..c59bf0798 100755 --- a/scripts/Transliteration/clean.pl +++ b/scripts/Transliteration/clean.pl @@ -1,6 +1,7 @@ #!/usr/bin/env perl #input hindi word urdu word, delete all those entries that have number on any side +use warnings; use utf8; use Getopt::Std; diff --git a/scripts/Transliteration/corpusCreator.pl b/scripts/Transliteration/corpusCreator.pl index 8c8dab863..d2df8323c 100755 --- a/scripts/Transliteration/corpusCreator.pl +++ b/scripts/Transliteration/corpusCreator.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use utf8; diff --git a/scripts/Transliteration/in-decoding-transliteration.pl b/scripts/Transliteration/in-decoding-transliteration.pl index e4e8b41e3..216d99a3e 100755 --- a/scripts/Transliteration/in-decoding-transliteration.pl +++ b/scripts/Transliteration/in-decoding-transliteration.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use utf8; diff --git a/scripts/Transliteration/post-decoding-transliteration.pl b/scripts/Transliteration/post-decoding-transliteration.pl index 7e6f249ae..201f40d97 100755 --- a/scripts/Transliteration/post-decoding-transliteration.pl +++ b/scripts/Transliteration/post-decoding-transliteration.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use utf8; diff --git a/scripts/Transliteration/prepare-transliteration-phrase-table.pl b/scripts/Transliteration/prepare-transliteration-phrase-table.pl index 565a98297..4fc03b526 100755 --- a/scripts/Transliteration/prepare-transliteration-phrase-table.pl +++ b/scripts/Transliteration/prepare-transliteration-phrase-table.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use utf8; diff --git a/scripts/Transliteration/threshold.pl b/scripts/Transliteration/threshold.pl index 8af699821..8e3704fd6 100755 --- a/scripts/Transliteration/threshold.pl +++ b/scripts/Transliteration/threshold.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use utf8; require Encode; use IO::Handle; diff --git a/scripts/Transliteration/train-transliteration-module.pl b/scripts/Transliteration/train-transliteration-module.pl index 54c2ccf78..05804afb6 100755 --- a/scripts/Transliteration/train-transliteration-module.pl +++ b/scripts/Transliteration/train-transliteration-module.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use utf8; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/analysis/bootstrap-hypothesis-difference-significance.pl b/scripts/analysis/bootstrap-hypothesis-difference-significance.pl index b74aa003d..149676b6f 100755 --- a/scripts/analysis/bootstrap-hypothesis-difference-significance.pl +++ b/scripts/analysis/bootstrap-hypothesis-difference-significance.pl @@ -14,6 +14,7 @@ use utf8; # 23.01.2010: added NIST p-value and interval computation ############################################### +use warnings; use strict; #constants diff --git a/scripts/analysis/sentence-by-sentence.pl b/scripts/analysis/sentence-by-sentence.pl index c8bc367b2..4f6560a56 100755 --- a/scripts/analysis/sentence-by-sentence.pl +++ b/scripts/analysis/sentence-by-sentence.pl @@ -4,6 +4,7 @@ #sentence-by-sentence: take in a system output, with any number of factors, and a reference translation, also maybe with factors, and show each sentence and its errors #usage: sentence-by-sentence SYSOUT [REFERENCE]+ > sentences.html +use warnings; use strict; use Getopt::Long; diff --git a/scripts/analysis/sg2dot.perl b/scripts/analysis/sg2dot.perl index a165cf25e..b17dfd9fb 100755 --- a/scripts/analysis/sg2dot.perl +++ b/scripts/analysis/sg2dot.perl @@ -4,6 +4,7 @@ # Script to convert MOSES searchgraph to DOT format # +use warnings; use strict; use File::Path; use File::Basename; diff --git a/scripts/analysis/show-phrases-used.pl b/scripts/analysis/show-phrases-used.pl index c31e930d5..0a719d207 100755 --- a/scripts/analysis/show-phrases-used.pl +++ b/scripts/analysis/show-phrases-used.pl @@ -5,7 +5,9 @@ #usage: show-phrases-used DECODER_OUTFILE > output.html # where DECODER_OUTFILE is the output of moses with the -T (show alignments) option +use warnings; use strict; + BEGIN { my $wd= `pawd 2>/dev/null`; diff --git a/scripts/analysis/smtgui/filter-phrase-table.pl b/scripts/analysis/smtgui/filter-phrase-table.pl index ed09d0b3f..9f411f3fa 100755 --- a/scripts/analysis/smtgui/filter-phrase-table.pl +++ b/scripts/analysis/smtgui/filter-phrase-table.pl @@ -9,6 +9,7 @@ #similar function to filter-model-given-input.pl, but only operates #on the phrase table and doesn't require that any subdirectories exist +use warnings; use strict; my $MAX_LENGTH = 10; diff --git a/scripts/ems/experiment.perl b/scripts/ems/experiment.perl index a7ce88622..7070a7c9e 100755 --- a/scripts/ems/experiment.perl +++ b/scripts/ems/experiment.perl @@ -3,6 +3,7 @@ # Experiment Management System # Documentation at http://www.statmt.org/moses/?n=FactoredTraining.EMS +use warnings; use strict; use Getopt::Long "GetOptions"; use FindBin qw($RealBin); diff --git a/scripts/ems/fix-info.perl b/scripts/ems/fix-info.perl index 98139f211..8f83d4ccf 100755 --- a/scripts/ems/fix-info.perl +++ b/scripts/ems/fix-info.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($file,$step) = @ARGV; diff --git a/scripts/ems/support/analysis.perl b/scripts/ems/support/analysis.perl index 8df3d6551..cea2657c9 100755 --- a/scripts/ems/support/analysis.perl +++ b/scripts/ems/support/analysis.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/ems/support/build-domain-file-from-subcorpora.perl b/scripts/ems/support/build-domain-file-from-subcorpora.perl index 683ef1ed7..f166c8927 100755 --- a/scripts/ems/support/build-domain-file-from-subcorpora.perl +++ b/scripts/ems/support/build-domain-file-from-subcorpora.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; # Create domain file from corpora diff --git a/scripts/ems/support/build-sparse-features.perl b/scripts/ems/support/build-sparse-features.perl index 04da69873..5d9b786ad 100755 --- a/scripts/ems/support/build-sparse-features.perl +++ b/scripts/ems/support/build-sparse-features.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; # Build necessary files for sparse lexical features diff --git a/scripts/ems/support/consolidate-training-data.perl b/scripts/ems/support/consolidate-training-data.perl index f312b1649..170ba999c 100755 --- a/scripts/ems/support/consolidate-training-data.perl +++ b/scripts/ems/support/consolidate-training-data.perl @@ -2,6 +2,7 @@ # $Id: consolidate-training-data.perl 928 2009-09-02 02:58:01Z philipp $ +use warnings; use strict; my ($in,$out,$consolidated,@PART) = @ARGV; diff --git a/scripts/ems/support/generic-multicore-parallelizer.perl b/scripts/ems/support/generic-multicore-parallelizer.perl index 073e0f62e..e5a12adce 100755 --- a/scripts/ems/support/generic-multicore-parallelizer.perl +++ b/scripts/ems/support/generic-multicore-parallelizer.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $cores = 8; diff --git a/scripts/ems/support/generic-parallelizer.perl b/scripts/ems/support/generic-parallelizer.perl index db4d2f492..0b248be7e 100755 --- a/scripts/ems/support/generic-parallelizer.perl +++ b/scripts/ems/support/generic-parallelizer.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $jobs = 20; diff --git a/scripts/ems/support/input-from-sgm.perl b/scripts/ems/support/input-from-sgm.perl index 81f177d6c..223996676 100755 --- a/scripts/ems/support/input-from-sgm.perl +++ b/scripts/ems/support/input-from-sgm.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; die("ERROR syntax: input-from-sgm.perl < in.sgm > in.txt") diff --git a/scripts/ems/support/interpolate-lm.perl b/scripts/ems/support/interpolate-lm.perl index 34bd2219d..a2fe62b22 100755 --- a/scripts/ems/support/interpolate-lm.perl +++ b/scripts/ems/support/interpolate-lm.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use IPC::Open3; use File::Temp qw/tempdir/; diff --git a/scripts/ems/support/lmplz-wrapper.perl b/scripts/ems/support/lmplz-wrapper.perl index 0fa676ce8..f79399d6b 100755 --- a/scripts/ems/support/lmplz-wrapper.perl +++ b/scripts/ems/support/lmplz-wrapper.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/ems/support/mml-filter.perl b/scripts/ems/support/mml-filter.perl index 5b6e02834..c50725aae 100755 --- a/scripts/ems/support/mml-filter.perl +++ b/scripts/ems/support/mml-filter.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use FindBin qw($RealBin); diff --git a/scripts/ems/support/mml-score.perl b/scripts/ems/support/mml-score.perl index 1fe065586..449d6a05c 100755 --- a/scripts/ems/support/mml-score.perl +++ b/scripts/ems/support/mml-score.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; # diff --git a/scripts/ems/support/mml-train.perl b/scripts/ems/support/mml-train.perl index aacf153a7..1f0548082 100755 --- a/scripts/ems/support/mml-train.perl +++ b/scripts/ems/support/mml-train.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($indomain_source,,$indomain_target,$outdomain_source,$outdomain_target,$lm_training,$lm_binarizer,$order,$lm_settings,$line_count,$model); diff --git a/scripts/ems/support/prepare-fast-align.perl b/scripts/ems/support/prepare-fast-align.perl index 1d6e75422..4ecc14cd7 100755 --- a/scripts/ems/support/prepare-fast-align.perl +++ b/scripts/ems/support/prepare-fast-align.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($source_file,$target_file,$alignment_factors) = @ARGV; diff --git a/scripts/ems/support/reference-from-sgm.perl b/scripts/ems/support/reference-from-sgm.perl index 0749648c0..595226bf1 100755 --- a/scripts/ems/support/reference-from-sgm.perl +++ b/scripts/ems/support/reference-from-sgm.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; die("ERROR syntax: reference-from-sgm.perl ref src out") diff --git a/scripts/ems/support/remove-segmentation-markup.perl b/scripts/ems/support/remove-segmentation-markup.perl index b345c9a7e..d6333f813 100755 --- a/scripts/ems/support/remove-segmentation-markup.perl +++ b/scripts/ems/support/remove-segmentation-markup.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; $|++; diff --git a/scripts/ems/support/report-experiment-scores.perl b/scripts/ems/support/report-experiment-scores.perl index 5bcf32f48..2e433f291 100755 --- a/scripts/ems/support/report-experiment-scores.perl +++ b/scripts/ems/support/report-experiment-scores.perl @@ -2,6 +2,7 @@ # $Id: report-experiment-scores.perl 407 2008-11-10 14:43:31Z philipp $ +use warnings; use strict; my $email; diff --git a/scripts/ems/support/run-command-on-multiple-refsets.perl b/scripts/ems/support/run-command-on-multiple-refsets.perl index f8e211582..c3db3c4dc 100755 --- a/scripts/ems/support/run-command-on-multiple-refsets.perl +++ b/scripts/ems/support/run-command-on-multiple-refsets.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; die("ERROR: syntax: run-command-on-multiple-refsets.perl cmd in out") diff --git a/scripts/ems/support/run-wade.perl b/scripts/ems/support/run-wade.perl index cf4121a14..25cda3bb3 100755 --- a/scripts/ems/support/run-wade.perl +++ b/scripts/ems/support/run-wade.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use File::Temp qw/ tempfile tempdir /; diff --git a/scripts/ems/support/split-sentences.perl b/scripts/ems/support/split-sentences.perl index 6537e84b3..f1af451b3 100755 --- a/scripts/ems/support/split-sentences.perl +++ b/scripts/ems/support/split-sentences.perl @@ -6,6 +6,7 @@ binmode(STDIN, ":utf8"); binmode(STDOUT, ":utf8"); binmode(STDERR, ":utf8"); +use warnings; use FindBin qw($RealBin); use strict; diff --git a/scripts/ems/support/submit-grid.perl b/scripts/ems/support/submit-grid.perl index 6e6193674..087d41647 100755 --- a/scripts/ems/support/submit-grid.perl +++ b/scripts/ems/support/submit-grid.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Cwd; use FindBin qw($RealBin); diff --git a/scripts/ems/support/substitute-filtered-tables-and-weights.perl b/scripts/ems/support/substitute-filtered-tables-and-weights.perl index 9c06b54f8..681d251c7 100755 --- a/scripts/ems/support/substitute-filtered-tables-and-weights.perl +++ b/scripts/ems/support/substitute-filtered-tables-and-weights.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; use FindBin qw($RealBin); diff --git a/scripts/ems/support/substitute-filtered-tables.perl b/scripts/ems/support/substitute-filtered-tables.perl index eee454728..e7d9f55f8 100755 --- a/scripts/ems/support/substitute-filtered-tables.perl +++ b/scripts/ems/support/substitute-filtered-tables.perl @@ -1,5 +1,7 @@ #!/usr/bin/env perl +use warnings; + # experiment.perl support script # get filtered rule and reordering tables and place them into a configuration file diff --git a/scripts/ems/support/substitute-weights.perl b/scripts/ems/support/substitute-weights.perl index 24ac034e8..42357ed1e 100755 --- a/scripts/ems/support/substitute-weights.perl +++ b/scripts/ems/support/substitute-weights.perl @@ -1,5 +1,7 @@ #!/usr/bin/env perl +use warnings; + # experiment.perl support script # get filtered rule and reordering tables and place them into a configuration file diff --git a/scripts/ems/support/symmetrize-fast-align.perl b/scripts/ems/support/symmetrize-fast-align.perl index f93af642d..90621dea9 100755 --- a/scripts/ems/support/symmetrize-fast-align.perl +++ b/scripts/ems/support/symmetrize-fast-align.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; die("ERROR: syntax is fastalign2bal.perl direct-alignment inverse-alignment source-file target-file out-stem symmetrization-method symal\n") unless scalar(@ARGV) == 7; diff --git a/scripts/ems/support/thot-lm-wrapper.perl b/scripts/ems/support/thot-lm-wrapper.perl index bd1f89c7b..222623c5b 100755 --- a/scripts/ems/support/thot-lm-wrapper.perl +++ b/scripts/ems/support/thot-lm-wrapper.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/ems/support/wrap-xml.perl b/scripts/ems/support/wrap-xml.perl index 587e4c541..28708a62a 100755 --- a/scripts/ems/support/wrap-xml.perl +++ b/scripts/ems/support/wrap-xml.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($language,$src,$system) = @ARGV; diff --git a/scripts/ems/web/progress.perl b/scripts/ems/web/progress.perl index 0612a0a44..fd742e410 100755 --- a/scripts/ems/web/progress.perl +++ b/scripts/ems/web/progress.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Date::Parse; diff --git a/scripts/fuzzy-match/create_xml.perl b/scripts/fuzzy-match/create_xml.perl index 56d4dff0f..80a1b3120 100755 --- a/scripts/fuzzy-match/create_xml.perl +++ b/scripts/fuzzy-match/create_xml.perl @@ -3,6 +3,7 @@ binmode( STDIN, ":utf8" ); binmode( STDOUT, ":utf8" ); +use warnings; use strict; use FindBin qw($RealBin); use File::Basename; diff --git a/scripts/generic/compound-splitter.perl b/scripts/generic/compound-splitter.perl index bbbccc8ef..c0b25f519 100755 --- a/scripts/generic/compound-splitter.perl +++ b/scripts/generic/compound-splitter.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/generic/extract-factors.pl b/scripts/generic/extract-factors.pl index 566849053..56c719051 100755 --- a/scripts/generic/extract-factors.pl +++ b/scripts/generic/extract-factors.pl @@ -6,6 +6,7 @@ #factor indices start at 0 #factor indices too large ought to be ignored +use warnings; use strict; my ($filename, @factors) = @ARGV; diff --git a/scripts/generic/extract-parallel.perl b/scripts/generic/extract-parallel.perl index 3c7429212..2b02fa869 100755 --- a/scripts/generic/extract-parallel.perl +++ b/scripts/generic/extract-parallel.perl @@ -3,6 +3,7 @@ # example # ./extract-parallel.perl 8 ./coreutils-8.9/src/split "./coreutils-8.9/src/sort --batch-size=253" ./extract ./corpus.5.en ./corpus.5.ar ./align.ar-en.grow-diag-final-and ./extracted 7 --NoFileLimit orientation --GZOutput +use warnings; use strict; use File::Basename; diff --git a/scripts/generic/fsa2fsal.pl b/scripts/generic/fsa2fsal.pl index 8cfdc0462..50bff1404 100755 --- a/scripts/generic/fsa2fsal.pl +++ b/scripts/generic/fsa2fsal.pl @@ -5,6 +5,7 @@ # Some rudimentary sanity checks are done on the fly. # Ondrej Bojar, bojar@ufal.mff.cuni.cz +use warnings; use strict; my $errs = 0; diff --git a/scripts/generic/fsa2plf.pl b/scripts/generic/fsa2plf.pl index 1177b01d5..4e7454a9f 100755 --- a/scripts/generic/fsa2plf.pl +++ b/scripts/generic/fsa2plf.pl @@ -8,6 +8,7 @@ # Note that the output format may not contain any spaces. # Ondrej Bojar, bojar@ufal.mff.cuni.cz +use warnings; use strict; use Getopt::Long; diff --git a/scripts/generic/fsal2fsa.pl b/scripts/generic/fsal2fsa.pl index 26258587d..d1aa461ac 100755 --- a/scripts/generic/fsal2fsa.pl +++ b/scripts/generic/fsal2fsa.pl @@ -2,6 +2,7 @@ # A very simple script that converts fsal back to fsa format (openfst lattices) # Ondrej Bojar, bojar@ufal.mff.cuni.cz +use warnings; use strict; while (<>) { diff --git a/scripts/generic/generic-parallel.perl b/scripts/generic/generic-parallel.perl index b7dca1bc9..653912c5c 100755 --- a/scripts/generic/generic-parallel.perl +++ b/scripts/generic/generic-parallel.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use utf8; diff --git a/scripts/generic/giza-parallel.perl b/scripts/generic/giza-parallel.perl index b5575e4d0..8793d3d8e 100755 --- a/scripts/generic/giza-parallel.perl +++ b/scripts/generic/giza-parallel.perl @@ -3,6 +3,7 @@ # example # ~/giza-parallel.perl 10 split ~/workspace/sourceforge/trunk/scripts/training/train-model.perl ar en train align +use warnings; use strict; use File::Basename; diff --git a/scripts/generic/lopar2pos.pl b/scripts/generic/lopar2pos.pl index d95389c05..c75069135 100755 --- a/scripts/generic/lopar2pos.pl +++ b/scripts/generic/lopar2pos.pl @@ -4,6 +4,8 @@ #lopar2pos: extract POSs from LOPAR output #usage: lopar2pos.pl CORPUS.lopar > CORPUS.pos +use warnings; + my $infilename = shift @ARGV; open(INFILE, "<$infilename") or die "couldn't open '$infilename' for read: $!\n"; while(my $line = ) diff --git a/scripts/generic/moses-parallel.pl b/scripts/generic/moses-parallel.pl index 4890864aa..7c0f56c70 100755 --- a/scripts/generic/moses-parallel.pl +++ b/scripts/generic/moses-parallel.pl @@ -15,6 +15,7 @@ # added checks for existence of decoder and configuration file # 26 Jul 2006 fix a bug related to the use of absolute path for srcfile and nbestfile +use warnings; use strict; ####################### diff --git a/scripts/generic/mteval-v12.pl b/scripts/generic/mteval-v12.pl index 0c771fc77..360376242 100755 --- a/scripts/generic/mteval-v12.pl +++ b/scripts/generic/mteval-v12.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use utf8; use Encode; diff --git a/scripts/generic/multi-bleu.perl b/scripts/generic/multi-bleu.perl index 5ed6add74..2f44d419f 100755 --- a/scripts/generic/multi-bleu.perl +++ b/scripts/generic/multi-bleu.perl @@ -1,6 +1,7 @@ #!/usr/bin/env perl # $Id$ +use warnings; use strict; my $lowercase = 0; diff --git a/scripts/generic/ph_numbers.perl b/scripts/generic/ph_numbers.perl index b33cd2805..ea56927ac 100755 --- a/scripts/generic/ph_numbers.perl +++ b/scripts/generic/ph_numbers.perl @@ -7,6 +7,7 @@ package ph_numbers; # # (c) 2013 TAUS +use warnings; use strict; run() unless caller(); diff --git a/scripts/generic/qsub-wrapper.pl b/scripts/generic/qsub-wrapper.pl index c5b63a71b..622323bdb 100755 --- a/scripts/generic/qsub-wrapper.pl +++ b/scripts/generic/qsub-wrapper.pl @@ -1,6 +1,7 @@ #!/usr/bin/env perl # $Id$ +use warnings; use strict; ####################### diff --git a/scripts/generic/reverse-alignment.perl b/scripts/generic/reverse-alignment.perl index fc8c33dff..d00140c74 100755 --- a/scripts/generic/reverse-alignment.perl +++ b/scripts/generic/reverse-alignment.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $line; diff --git a/scripts/generic/score-parallel.perl b/scripts/generic/score-parallel.perl index 9979f087d..9e5ee0025 100755 --- a/scripts/generic/score-parallel.perl +++ b/scripts/generic/score-parallel.perl @@ -4,6 +4,7 @@ # ./score-parallel.perl 8 "gsort --batch-size=253" ./score ./extract.2.sorted.gz ./lex.2.f2e ./phrase-table.2.half.f2e --GoodTuring ./phrase-table.2.coc 0 # ./score-parallel.perl 8 "gsort --batch-size=253" ./score ./extract.2.inv.sorted.gz ./lex.2.e2f ./phrase-table.2.half.e2f --Inverse 1 +use warnings; use strict; use File::Basename; diff --git a/scripts/generic/strip-xml.perl b/scripts/generic/strip-xml.perl index 61b823ce2..95513b608 100755 --- a/scripts/generic/strip-xml.perl +++ b/scripts/generic/strip-xml.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; while (my $line = ) { diff --git a/scripts/generic/trainlm-irst2.perl b/scripts/generic/trainlm-irst2.perl index a84ea1c61..596143386 100755 --- a/scripts/generic/trainlm-irst2.perl +++ b/scripts/generic/trainlm-irst2.perl @@ -10,6 +10,7 @@ # irst-dir = /Users/hieu/workspace/irstlm/trunk/bin # Set smoothing method in settings, if different from modified Kneser-Ney +use warnings; use strict; use FindBin qw($RealBin); use Getopt::Long; diff --git a/scripts/generic/trainlm-lmplz.perl b/scripts/generic/trainlm-lmplz.perl index 045248675..c7e9e4553 100755 --- a/scripts/generic/trainlm-lmplz.perl +++ b/scripts/generic/trainlm-lmplz.perl @@ -9,6 +9,7 @@ # It should point to the binary file # lmplz = /home/waziz/workspace/github/moses/bin/lmplz +use warnings; use strict; use FindBin qw($RealBin); use Getopt::Long qw/GetOptionsFromArray/; diff --git a/scripts/other/beautify.perl b/scripts/other/beautify.perl index 73ea51beb..130afd56b 100755 --- a/scripts/other/beautify.perl +++ b/scripts/other/beautify.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use File::Basename; use FindBin qw($RealBin); diff --git a/scripts/other/delete-scores.perl b/scripts/other/delete-scores.perl index c0b723d64..08316c95b 100755 --- a/scripts/other/delete-scores.perl +++ b/scripts/other/delete-scores.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/other/get_many_translations_from_google.perl b/scripts/other/get_many_translations_from_google.perl index 6ef83e240..512b84e36 100755 --- a/scripts/other/get_many_translations_from_google.perl +++ b/scripts/other/get_many_translations_from_google.perl @@ -6,6 +6,7 @@ # # Ondrej Bojar, bojar@ufal.mff.cuni.cz +use warnings; use strict; use Getopt::Long; use CGI; diff --git a/scripts/recaser/detruecase.perl b/scripts/recaser/detruecase.perl index efa5e12b6..549cd8abe 100755 --- a/scripts/recaser/detruecase.perl +++ b/scripts/recaser/detruecase.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/recaser/recase.perl b/scripts/recaser/recase.perl index 0b1ded200..3ba83712a 100755 --- a/scripts/recaser/recase.perl +++ b/scripts/recaser/recase.perl @@ -1,6 +1,7 @@ #!/usr/bin/env perl # $Id$ +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/recaser/train-recaser.perl b/scripts/recaser/train-recaser.perl index 27c5da198..87a720f6e 100755 --- a/scripts/recaser/train-recaser.perl +++ b/scripts/recaser/train-recaser.perl @@ -1,6 +1,7 @@ #!/usr/bin/env perl # $Id$ +use warnings; use strict; use FindBin qw($Bin); use Getopt::Long "GetOptions"; diff --git a/scripts/recaser/train-truecaser.perl b/scripts/recaser/train-truecaser.perl index b6e5c3884..b653a8ca5 100755 --- a/scripts/recaser/train-truecaser.perl +++ b/scripts/recaser/train-truecaser.perl @@ -8,6 +8,7 @@ # --possiblyUseFirstToken : boolean option; the default behaviour (when this option is not provided) is that the first token of a sentence is ignored, on the basis that the first word of a sentence is always capitalized; if this option is provided then: a) if a sentence-initial token is *not* capitalized, then it is counted, and b) if a capitalized sentence-initial token is the only token of the segment, then it is counted, but with only 10% of the weight of a normal token. # +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/recaser/truecase.perl b/scripts/recaser/truecase.perl index d14d7ebe4..373aa509f 100755 --- a/scripts/recaser/truecase.perl +++ b/scripts/recaser/truecase.perl @@ -1,6 +1,8 @@ #!/usr/bin/env perl # $Id: train-recaser.perl 1326 2007-03-26 05:44:27Z bojar $ + +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/regression-testing/compare-results.pl b/scripts/regression-testing/compare-results.pl index 0d77ef8fc..df14d444f 100755 --- a/scripts/regression-testing/compare-results.pl +++ b/scripts/regression-testing/compare-results.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($results, $truth) = @ARGV; diff --git a/scripts/regression-testing/create_localized_moses_ini.pl b/scripts/regression-testing/create_localized_moses_ini.pl index 78a033b32..612a39e82 100755 --- a/scripts/regression-testing/create_localized_moses_ini.pl +++ b/scripts/regression-testing/create_localized_moses_ini.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $script_dir; BEGIN { use Cwd qw/ abs_path /; use File::Basename; $script_dir = dirname(abs_path($0)); push @INC, $script_dir; } use MosesScriptsRegressionTesting; diff --git a/scripts/regression-testing/modify-pars.pl b/scripts/regression-testing/modify-pars.pl index 4669ae0b6..5ad2514a4 100755 --- a/scripts/regression-testing/modify-pars.pl +++ b/scripts/regression-testing/modify-pars.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $argv=join(" ",@ARGV); diff --git a/scripts/regression-testing/moses-virtual.pl b/scripts/regression-testing/moses-virtual.pl index 55198900b..41ddd6b13 100755 --- a/scripts/regression-testing/moses-virtual.pl +++ b/scripts/regression-testing/moses-virtual.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my %opt = (); diff --git a/scripts/regression-testing/run-single-test.pl b/scripts/regression-testing/run-single-test.pl index 2fa7b4dce..bb66e96f6 100755 --- a/scripts/regression-testing/run-single-test.pl +++ b/scripts/regression-testing/run-single-test.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $script_dir; BEGIN { use Cwd qw/ abs_path /; use File::Basename; $script_dir = dirname(abs_path($0)); push @INC, $script_dir; } use MosesScriptsRegressionTesting; diff --git a/scripts/regression-testing/run-test-suite.pl b/scripts/regression-testing/run-test-suite.pl index d90dfa35d..8ae9ec60f 100755 --- a/scripts/regression-testing/run-test-suite.pl +++ b/scripts/regression-testing/run-test-suite.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $script_dir; BEGIN { use Cwd qw/ abs_path /; use File::Basename; $script_dir = dirname(abs_path($0)); push @INC, $script_dir; } use Getopt::Long; diff --git a/scripts/tokenizer/deescape-special-chars-PTB.perl b/scripts/tokenizer/deescape-special-chars-PTB.perl index 17fe650d2..0e73a7718 100755 --- a/scripts/tokenizer/deescape-special-chars-PTB.perl +++ b/scripts/tokenizer/deescape-special-chars-PTB.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; while() { diff --git a/scripts/tokenizer/deescape-special-chars.perl b/scripts/tokenizer/deescape-special-chars.perl index dc810d817..076d1e62f 100755 --- a/scripts/tokenizer/deescape-special-chars.perl +++ b/scripts/tokenizer/deescape-special-chars.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; while() { diff --git a/scripts/tokenizer/detokenizer.perl b/scripts/tokenizer/detokenizer.perl index 27e315840..7874d5d04 100755 --- a/scripts/tokenizer/detokenizer.perl +++ b/scripts/tokenizer/detokenizer.perl @@ -7,6 +7,8 @@ binmode(STDIN, ":utf8"); binmode(STDOUT, ":utf8"); + +use warnings; use strict; use utf8; # tell perl this script file is in UTF-8 (see all funny punct below) diff --git a/scripts/tokenizer/escape-special-chars.perl b/scripts/tokenizer/escape-special-chars.perl index 79ae39469..e94b91744 100755 --- a/scripts/tokenizer/escape-special-chars.perl +++ b/scripts/tokenizer/escape-special-chars.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; while() { diff --git a/scripts/tokenizer/lowercase.perl b/scripts/tokenizer/lowercase.perl index cb1250938..9ee307bc2 100755 --- a/scripts/tokenizer/lowercase.perl +++ b/scripts/tokenizer/lowercase.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; binmode(STDIN, ":utf8"); diff --git a/scripts/tokenizer/normalize-punctuation.perl b/scripts/tokenizer/normalize-punctuation.perl index 8f779449f..db8f9c60e 100755 --- a/scripts/tokenizer/normalize-punctuation.perl +++ b/scripts/tokenizer/normalize-punctuation.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $language = "en"; diff --git a/scripts/tokenizer/pre-tokenizer.perl b/scripts/tokenizer/pre-tokenizer.perl index 35134a9c0..499671b44 100755 --- a/scripts/tokenizer/pre-tokenizer.perl +++ b/scripts/tokenizer/pre-tokenizer.perl @@ -4,6 +4,7 @@ # Start by Ulrich Germann, after noticing systematic preprocessing errors # in some of the English Europarl data. +use warnings; use strict; use Getopt::Std; diff --git a/scripts/tokenizer/remove-non-printing-char.perl b/scripts/tokenizer/remove-non-printing-char.perl index 4dadd1d77..2b90dfd3b 100755 --- a/scripts/tokenizer/remove-non-printing-char.perl +++ b/scripts/tokenizer/remove-non-printing-char.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use utf8; binmode(STDIN, ":utf8"); diff --git a/scripts/tokenizer/replace-unicode-punctuation.perl b/scripts/tokenizer/replace-unicode-punctuation.perl index 748e1d063..08eb766bf 100755 --- a/scripts/tokenizer/replace-unicode-punctuation.perl +++ b/scripts/tokenizer/replace-unicode-punctuation.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; #binmode(STDIN, ":utf8"); diff --git a/scripts/tokenizer/tokenizer.perl b/scripts/tokenizer/tokenizer.perl index eeede0af0..8abffbea4 100755 --- a/scripts/tokenizer/tokenizer.perl +++ b/scripts/tokenizer/tokenizer.perl @@ -16,6 +16,7 @@ use warnings; binmode(STDIN, ":utf8"); binmode(STDOUT, ":utf8"); +use warnings; use FindBin qw($RealBin); use strict; use Time::HiRes; diff --git a/scripts/tokenizer/tokenizer_PTB.perl b/scripts/tokenizer/tokenizer_PTB.perl index 6417b7d6e..bce7a38a0 100755 --- a/scripts/tokenizer/tokenizer_PTB.perl +++ b/scripts/tokenizer/tokenizer_PTB.perl @@ -14,6 +14,7 @@ binmode(STDIN, ":utf8"); binmode(STDOUT, ":utf8"); +use warnings; use FindBin qw($RealBin); use strict; use Time::HiRes; diff --git a/scripts/training/absolutize_moses_model.pl b/scripts/training/absolutize_moses_model.pl index ecfcb3395..5c9c0970a 100755 --- a/scripts/training/absolutize_moses_model.pl +++ b/scripts/training/absolutize_moses_model.pl @@ -6,6 +6,8 @@ # # Ondrej Bojar. +use warnings; + my $ini = shift; die "usage: absolutize_moses_model.pl path-to-moses.ini > moses.abs.ini" if !defined $ini; diff --git a/scripts/training/binarize-model.perl b/scripts/training/binarize-model.perl index 0246190f2..3d4798ffd 100755 --- a/scripts/training/binarize-model.perl +++ b/scripts/training/binarize-model.perl @@ -4,6 +4,7 @@ # Binarize a Moses model # +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/training/build-generation-table.perl b/scripts/training/build-generation-table.perl index 8b1303795..fb59f4acc 100755 --- a/scripts/training/build-generation-table.perl +++ b/scripts/training/build-generation-table.perl @@ -1,6 +1,7 @@ #!/usr/bin/env perl # $Id$ +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/training/build-mmsapt.perl b/scripts/training/build-mmsapt.perl index bd8d1078f..a7ddaff70 100755 --- a/scripts/training/build-mmsapt.perl +++ b/scripts/training/build-mmsapt.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; use FindBin qw($RealBin); diff --git a/scripts/training/clean-corpus-n.perl b/scripts/training/clean-corpus-n.perl index 40e4d8935..e1e96528c 100755 --- a/scripts/training/clean-corpus-n.perl +++ b/scripts/training/clean-corpus-n.perl @@ -1,6 +1,7 @@ #!/usr/bin/env perl # $Id: clean-corpus-n.perl 3633 2010-10-21 09:49:27Z phkoehn $ +use warnings; use strict; use Getopt::Long; my $help; diff --git a/scripts/training/clone_moses_model.pl b/scripts/training/clone_moses_model.pl index 93e37b803..5e9dff72a 100755 --- a/scripts/training/clone_moses_model.pl +++ b/scripts/training/clone_moses_model.pl @@ -5,6 +5,7 @@ # in the current directory # All relevant files are hardlinked or copied to the directory, too. +use warnings; use strict; use Getopt::Long; diff --git a/scripts/training/convert-moses-ini-to-v2.perl b/scripts/training/convert-moses-ini-to-v2.perl index 1bc4fe79d..25c562ef4 100755 --- a/scripts/training/convert-moses-ini-to-v2.perl +++ b/scripts/training/convert-moses-ini-to-v2.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $header = ""; diff --git a/scripts/training/corpus-sizes.perl b/scripts/training/corpus-sizes.perl index 1eccf9bd5..02dd4ae9b 100755 --- a/scripts/training/corpus-sizes.perl +++ b/scripts/training/corpus-sizes.perl @@ -2,6 +2,7 @@ # $Id: consolidate-training-data.perl 928 2009-09-02 02:58:01Z philipp $ +use warnings; use strict; my ($in,$out,@PART) = @ARGV; diff --git a/scripts/training/exodus.perl b/scripts/training/exodus.perl index ef3d8df92..d3466f5dd 100755 --- a/scripts/training/exodus.perl +++ b/scripts/training/exodus.perl @@ -2,6 +2,7 @@ # $Id$ +use warnings; use strict; my @LINE = ; diff --git a/scripts/training/filter-model-given-input.pl b/scripts/training/filter-model-given-input.pl index dbafc73be..7dec0762c 100755 --- a/scripts/training/filter-model-given-input.pl +++ b/scripts/training/filter-model-given-input.pl @@ -8,6 +8,7 @@ # changes by Ondrej Bojar # adapted for hierarchical models by Phil Williams +use warnings; use strict; use FindBin qw($RealBin); diff --git a/scripts/training/get-lexical.perl b/scripts/training/get-lexical.perl index 2dcf7437f..45fe6d54c 100755 --- a/scripts/training/get-lexical.perl +++ b/scripts/training/get-lexical.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use FindBin qw($RealBin); BEGIN { require "$RealBin/LexicalTranslationModel.pm"; "LexicalTranslationModel"->import; } diff --git a/scripts/training/giza2bal.pl b/scripts/training/giza2bal.pl index 8b2150e31..8daa8e916 100755 --- a/scripts/training/giza2bal.pl +++ b/scripts/training/giza2bal.pl @@ -7,6 +7,8 @@ #Copyright Marcello Federico, November 2004 +use warnings; + ($cnt,$dir,$inv)=(); while ($w=shift @ARGV){ diff --git a/scripts/training/mert-moses.pl b/scripts/training/mert-moses.pl index 5d1f9b368..86084abbf 100755 --- a/scripts/training/mert-moses.pl +++ b/scripts/training/mert-moses.pl @@ -47,6 +47,7 @@ # 13 Oct 2004 Use alternative decoders (DWC) # Original version by Philipp Koehn +use warnings; use strict; use FindBin qw($RealBin); use File::Basename; diff --git a/scripts/training/postprocess-lopar.perl b/scripts/training/postprocess-lopar.perl index 9962d5594..5171e02fb 100755 --- a/scripts/training/postprocess-lopar.perl +++ b/scripts/training/postprocess-lopar.perl @@ -2,6 +2,7 @@ # $Id$ +use warnings; use strict; use utf8; diff --git a/scripts/training/reduce-factors.perl b/scripts/training/reduce-factors.perl index bc08a3a9d..c265652f6 100755 --- a/scripts/training/reduce-factors.perl +++ b/scripts/training/reduce-factors.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; use FindBin qw($RealBin); diff --git a/scripts/training/remove-orphan-phrase-pairs-from-reordering-table.perl b/scripts/training/remove-orphan-phrase-pairs-from-reordering-table.perl index 2f412cd28..bd5d7f1d2 100755 --- a/scripts/training/remove-orphan-phrase-pairs-from-reordering-table.perl +++ b/scripts/training/remove-orphan-phrase-pairs-from-reordering-table.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($ttable_file) = @ARGV; diff --git a/scripts/training/threshold-filter.perl b/scripts/training/threshold-filter.perl index f8d15a8ae..a23fb8b5c 100755 --- a/scripts/training/threshold-filter.perl +++ b/scripts/training/threshold-filter.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my %MIN_SCORE; diff --git a/scripts/training/train-global-lexicon-model.perl b/scripts/training/train-global-lexicon-model.perl index 20ee42b72..0e7d3077d 100755 --- a/scripts/training/train-global-lexicon-model.perl +++ b/scripts/training/train-global-lexicon-model.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; use Switch; diff --git a/scripts/training/train-model.perl b/scripts/training/train-model.perl index ade5c5277..6f850d609 100755 --- a/scripts/training/train-model.perl +++ b/scripts/training/train-model.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; use FindBin qw($RealBin); diff --git a/scripts/training/wrappers/berkeleyparsed2mosesxml.perl b/scripts/training/wrappers/berkeleyparsed2mosesxml.perl index 3bbf982b7..3dd8fc4ac 100755 --- a/scripts/training/wrappers/berkeleyparsed2mosesxml.perl +++ b/scripts/training/wrappers/berkeleyparsed2mosesxml.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; while() { diff --git a/scripts/training/wrappers/berkeleyparsed2mosesxml_PTB.perl b/scripts/training/wrappers/berkeleyparsed2mosesxml_PTB.perl index 91fc515cb..e61a53652 100755 --- a/scripts/training/wrappers/berkeleyparsed2mosesxml_PTB.perl +++ b/scripts/training/wrappers/berkeleyparsed2mosesxml_PTB.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; while() { diff --git a/scripts/training/wrappers/filter-excluded-lines.perl b/scripts/training/wrappers/filter-excluded-lines.perl index 2f1e25ad4..7f9da3efa 100755 --- a/scripts/training/wrappers/filter-excluded-lines.perl +++ b/scripts/training/wrappers/filter-excluded-lines.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long; diff --git a/scripts/training/wrappers/find-unparseable.perl b/scripts/training/wrappers/find-unparseable.perl index 0bbf35df4..b0d38027b 100755 --- a/scripts/training/wrappers/find-unparseable.perl +++ b/scripts/training/wrappers/find-unparseable.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my $lineNum = 1; diff --git a/scripts/training/wrappers/mada-wrapper.perl b/scripts/training/wrappers/mada-wrapper.perl index eec10a3ef..20f76f821 100755 --- a/scripts/training/wrappers/mada-wrapper.perl +++ b/scripts/training/wrappers/mada-wrapper.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use File::Temp qw/tempfile/; use Getopt::Long "GetOptions"; diff --git a/scripts/training/wrappers/make-factor-brown-cluster-mkcls.perl b/scripts/training/wrappers/make-factor-brown-cluster-mkcls.perl index cf7473e44..88d16b3f6 100755 --- a/scripts/training/wrappers/make-factor-brown-cluster-mkcls.perl +++ b/scripts/training/wrappers/make-factor-brown-cluster-mkcls.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($lowercase, $cluster_file,$in,$out,$tmp) = @ARGV; diff --git a/scripts/training/wrappers/make-factor-de-morph.perl b/scripts/training/wrappers/make-factor-de-morph.perl index 4b2c90495..1cc917bce 100755 --- a/scripts/training/wrappers/make-factor-de-morph.perl +++ b/scripts/training/wrappers/make-factor-de-morph.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Encode; use FindBin qw($RealBin); diff --git a/scripts/training/wrappers/make-factor-de-pos.perl b/scripts/training/wrappers/make-factor-de-pos.perl index 8cc28695a..2eadd4123 100755 --- a/scripts/training/wrappers/make-factor-de-pos.perl +++ b/scripts/training/wrappers/make-factor-de-pos.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($in,$out,$tmpdir) = @ARGV; diff --git a/scripts/training/wrappers/make-factor-en-pos.mxpost.perl b/scripts/training/wrappers/make-factor-en-pos.mxpost.perl index 3ab2b1ca4..0d27aa12f 100755 --- a/scripts/training/wrappers/make-factor-en-pos.mxpost.perl +++ b/scripts/training/wrappers/make-factor-en-pos.mxpost.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use FindBin qw($RealBin); diff --git a/scripts/training/wrappers/make-factor-pos.tree-tagger.perl b/scripts/training/wrappers/make-factor-pos.tree-tagger.perl index 1e00a8fa3..2af6eb75c 100755 --- a/scripts/training/wrappers/make-factor-pos.tree-tagger.perl +++ b/scripts/training/wrappers/make-factor-pos.tree-tagger.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; # handle switches diff --git a/scripts/training/wrappers/make-factor-stem.perl b/scripts/training/wrappers/make-factor-stem.perl index c222ad0df..60aca0b34 100755 --- a/scripts/training/wrappers/make-factor-stem.perl +++ b/scripts/training/wrappers/make-factor-stem.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($size,$in,$out) = @ARGV; diff --git a/scripts/training/wrappers/make-factor-suffix.perl b/scripts/training/wrappers/make-factor-suffix.perl index d13c43230..7e864ea0c 100755 --- a/scripts/training/wrappers/make-factor-suffix.perl +++ b/scripts/training/wrappers/make-factor-suffix.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; my ($size,$in,$out) = @ARGV; diff --git a/scripts/training/wrappers/mosesxml2berkeleyparsed.perl b/scripts/training/wrappers/mosesxml2berkeleyparsed.perl index f7855e06d..fc1f0c532 100755 --- a/scripts/training/wrappers/mosesxml2berkeleyparsed.perl +++ b/scripts/training/wrappers/mosesxml2berkeleyparsed.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; #( (NP (NP (NN resumption)) (PP (IN of) (NP (DT the) (NN session)))) ) diff --git a/scripts/training/wrappers/parse-de-berkeley.perl b/scripts/training/wrappers/parse-de-berkeley.perl index b8b546953..68df07c49 100755 --- a/scripts/training/wrappers/parse-de-berkeley.perl +++ b/scripts/training/wrappers/parse-de-berkeley.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; use FindBin qw($RealBin); diff --git a/scripts/training/wrappers/parse-de-bitpar.perl b/scripts/training/wrappers/parse-de-bitpar.perl index 8cb34055c..4723d6aa0 100755 --- a/scripts/training/wrappers/parse-de-bitpar.perl +++ b/scripts/training/wrappers/parse-de-bitpar.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; use FindBin qw($RealBin); diff --git a/scripts/training/wrappers/parse-en-collins.perl b/scripts/training/wrappers/parse-en-collins.perl index 3d879c06b..27b33a2dd 100755 --- a/scripts/training/wrappers/parse-en-collins.perl +++ b/scripts/training/wrappers/parse-en-collins.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use File::Basename; use File::Temp qw/tempfile/; diff --git a/scripts/training/wrappers/parse-en-egret.perl b/scripts/training/wrappers/parse-en-egret.perl index fc330c70f..c3d23a4ee 100755 --- a/scripts/training/wrappers/parse-en-egret.perl +++ b/scripts/training/wrappers/parse-en-egret.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; use FindBin qw($RealBin); diff --git a/scripts/training/wrappers/syntax-hyphen-splitting.perl b/scripts/training/wrappers/syntax-hyphen-splitting.perl index 2c830f6b6..1bb616939 100755 --- a/scripts/training/wrappers/syntax-hyphen-splitting.perl +++ b/scripts/training/wrappers/syntax-hyphen-splitting.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; diff --git a/scripts/training/wrappers/tagger-german-chunk.perl b/scripts/training/wrappers/tagger-german-chunk.perl index b6b2871ba..4f26efabe 100755 --- a/scripts/training/wrappers/tagger-german-chunk.perl +++ b/scripts/training/wrappers/tagger-german-chunk.perl @@ -1,5 +1,6 @@ #!/usr/bin/env perl +use warnings; use strict; use Getopt::Long "GetOptions"; From 7af653ac80a0e4ec4cf19beda65b0c01624624d7 Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Tue, 14 Apr 2015 11:29:56 +0400 Subject: [PATCH 17/53] misc script to parallelize madamira on grid engine --- contrib/mada/qsub-madamira.perl | 46 +++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100755 contrib/mada/qsub-madamira.perl diff --git a/contrib/mada/qsub-madamira.perl b/contrib/mada/qsub-madamira.perl new file mode 100755 index 000000000..bb7ecd06b --- /dev/null +++ b/contrib/mada/qsub-madamira.perl @@ -0,0 +1,46 @@ +#!/usr/bin/env perl + +use warnings; +use strict; +use File::Slurp; +use File::Basename; +use Cwd 'abs_path'; + +my $splitDir = $ARGV[0]; +$splitDir = abs_path($splitDir); + +my @files = read_dir $splitDir; + +my $qsubDir=dirname($splitDir) ."/qsub"; +print STDERR "qsubDir=$qsubDir\n"; +`mkdir -p $qsubDir`; + +my $out2Dir=dirname($splitDir) ."/out2"; +print STDERR "out2Dir=$out2Dir\n"; +`mkdir -p $out2Dir`; + +for my $file ( @files ) { + print STDERR "$file "; + + my $qsubFile = "$qsubDir/$file.sh"; + open(RUN_FILE, ">$qsubFile"); + + print RUN_FILE "#!/usr/bin/env bash\n" + ."#PBS -d/scratch/hh65/workspace/experiment/ar-en \n" + ."#PBS -l mem=5gb \n\n" + ."export PATH=\"/scratch/statmt/bin:/share/apps/NYUAD/perl/gcc_4.9.1/5.20.1/bin:/share/apps/NYUAD/jdk/1.8.0_31/bin:/share/apps/NYUAD/zlib/gcc_4.9.1/1.2.8/bin:/share/apps/NYUAD/cmake/gcc_4.9.1/3.1.0-rc3/bin:/share/apps/NYUAD/boost/gcc_4.9.1/openmpi_1.8.3/1.57.0/bin:/share/apps/NYUAD/openmpi/gcc_4.9.1/1.8.3/bin:/share/apps/NYUAD/python/gcc_4.9.1/2.7.9/bin:/share/apps/NYUAD/gcc/binutils/2.21/el6/bin:/share/apps/NYUAD/gcc/gcc/4.9.1/el6/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/bio/ncbi/bin:/opt/bio/mpiblast/bin:/opt/bio/EMBOSS/bin:/opt/bio/clustalw/bin:/opt/bio/tcoffee/bin:/opt/bio/hmmer/bin:/opt/bio/phylip/exe:/opt/bio/mrbayes:/opt/bio/fasta:/opt/bio/glimmer/bin:/opt/bio/glimmer/scripts:/opt/bio/gromacs/bin:/opt/bio/gmap/bin:/opt/bio/tigr/bin:/opt/bio/autodocksuite/bin:/opt/bio/wgs/bin:/opt/ganglia/bin:/opt/ganglia/sbin:/opt/bin:/usr/java/latest/bin:/opt/pdsh/bin:/opt/rocks/bin:/opt/rocks/sbin:/opt/torque/bin:/opt/torque/sbin:/home/hh65/bin:/home/hh65/bin\" \n" + + ."module load NYUAD/2.0 \n" + ."module load gcc python/2.7.9 openmpi/1.8.3 boost cmake zlib jdk perl expat \n" + + ."cd /scratch/statmt/MADAMIRA-release-20140709-1.0 \n"; + print RUN_FILE "java -Xmx2500m -Xms2500m -XX:NewRatio=3 -jar /scratch/statmt/MADAMIRA-release-20140709-1.0/MADAMIRA.jar " + ."-rawinput $splitDir/$file -rawoutdir $out2Dir -rawconfig /scratch/statmt/MADAMIRA-release-20140709-1.0/samples/sampleConfigFile.xml \n"; + + close(RUN_FILE); + + my $cmd = "qsub $qsubFile"; + `$cmd`; + +} + From c544dba7ad0856ce438870d7bec17c7249fed8c7 Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Wed, 15 Apr 2015 18:53:13 +0400 Subject: [PATCH 18/53] don't use /dev/stdin and /dev/stdout. Compatibility issues with some Redhat --- symal/symal.cpp | 64 +++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/symal/symal.cpp b/symal/symal.cpp index dbe68f1b9..b0cec8251 100644 --- a/symal/symal.cpp +++ b/symal/symal.cpp @@ -67,7 +67,7 @@ int verbose=0; int lc = 0; -int getals(fstream& inp,int& m, int *a,int& n, int *b) +int getals(istream& inp,int& m, int *a,int& n, int *b) { char w[MAX_WORD], dummy[10]; int i,j,freq; @@ -121,7 +121,7 @@ int getals(fstream& inp,int& m, int *a,int& n, int *b) //compute union alignment -int prunionalignment(fstream& out,int m,int *a,int n,int* b) +int prunionalignment(ostream& out,int m,int *a,int n,int* b) { ostringstream sout; @@ -150,7 +150,7 @@ int prunionalignment(fstream& out,int m,int *a,int n,int* b) //Compute intersection alignment -int printersect(fstream& out,int m,int *a,int n,int* b) +int printersect(ostream& out,int m,int *a,int n,int* b) { ostringstream sout; @@ -174,7 +174,7 @@ int printersect(fstream& out,int m,int *a,int n,int* b) //Compute target-to-source alignment -int printtgttosrc(fstream& out,int m,int *a,int n,int* b) +int printtgttosrc(ostream& out,int m,int *a,int n,int* b) { ostringstream sout; @@ -198,7 +198,7 @@ int printtgttosrc(fstream& out,int m,int *a,int n,int* b) //Compute source-to-target alignment -int printsrctotgt(fstream& out,int m,int *a,int n,int* b) +int printsrctotgt(ostream& out,int m,int *a,int n,int* b) { ostringstream sout; @@ -226,7 +226,7 @@ int printsrctotgt(fstream& out,int m,int *a,int n,int* b) //to represent the grow alignment as the unionalignment of a //directed and inverted alignment -int printgrow(fstream& out,int m,int *a,int n,int* b, bool diagonal=false,bool final=false,bool bothuncovered=false) +int printgrow(ostream& out,int m,int *a,int n,int* b, bool diagonal=false,bool final=false,bool bothuncovered=false) { ostringstream sout; @@ -392,8 +392,8 @@ int main(int argc, char** argv) { int alignment=0; - char* input=(char*)"/dev/stdin"; - char* output=(char*)"/dev/stdout"; + char* input= NULL; + char* output= NULL; int diagonal=false; int final=false; int bothuncovered=false; @@ -421,23 +421,31 @@ int main(int argc, char** argv) << "Input file or std must be in .bal format (see script giza2bal.pl).\n"; exit(1); - } - fstream inp(input,ios::in); - fstream out(output,ios::out); + istream *inp = &std::cin; + ostream *out = &std::cout; + //fstream inp(input,ios::in); + //fstream out(output,ios::out); - if (!inp.is_open()) { - cerr << "cannot open " << input << "\n"; - exit(1); + if (input) { + fstream *fin = new fstream(input,ios::in); + if (!fin->is_open()) { + cerr << "cannot open " << input << "\n"; + exit(1); + } + //inp = *fin; } - if (!out.is_open()) { - cerr << "cannot open " << output << "\n"; - exit(1); + if (output) { + fstream *fout = new fstream(output,ios::out); + if (!fout->is_open()) { + cerr << "cannot open " << output << "\n"; + exit(1); + } + //out = *fout; } - int a[MAX_M],b[MAX_N],m,n; fa=new int[MAX_M+1]; ea=new int[MAX_N+1]; @@ -450,16 +458,16 @@ int main(int argc, char** argv) switch (alignment) { case UNION: cerr << "symal: computing union alignment\n"; - while(getals(inp,m,a,n,b)) { - prunionalignment(out,m,a,n,b); + while(getals(*inp,m,a,n,b)) { + prunionalignment(*out,m,a,n,b); sents++; } cerr << "Sents: " << sents << endl; break; case INTERSECT: cerr << "symal: computing intersect alignment\n"; - while(getals(inp,m,a,n,b)) { - printersect(out,m,a,n,b); + while(getals(*inp,m,a,n,b)) { + printersect(*out,m,a,n,b); sents++; } cerr << "Sents: " << sents << endl; @@ -469,15 +477,15 @@ int main(int argc, char** argv) << diagonal << ") final ("<< final << ")" << "both-uncovered (" << bothuncovered <<")\n"; - while(getals(inp,m,a,n,b)) - printgrow(out,m,a,n,b,diagonal,final,bothuncovered); + while(getals(*inp,m,a,n,b)) + printgrow(*out,m,a,n,b,diagonal,final,bothuncovered); break; case TGTTOSRC: cerr << "symal: computing target-to-source alignment\n"; - while(getals(inp,m,a,n,b)) { - printtgttosrc(out,m,a,n,b); + while(getals(*inp,m,a,n,b)) { + printtgttosrc(*out,m,a,n,b); sents++; } cerr << "Sents: " << sents << endl; @@ -485,8 +493,8 @@ int main(int argc, char** argv) case SRCTOTGT: cerr << "symal: computing source-to-target alignment\n"; - while(getals(inp,m,a,n,b)) { - printsrctotgt(out,m,a,n,b); + while(getals(*inp,m,a,n,b)) { + printsrctotgt(*out,m,a,n,b); sents++; } cerr << "Sents: " << sents << endl; From e279135d78ce2690bb17a5216d7eede715c77e1a Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Thu, 16 Apr 2015 00:50:54 +0400 Subject: [PATCH 19/53] create file stream and delete it at the end if user has specified a file for input & output --- symal/symal.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/symal/symal.cpp b/symal/symal.cpp index b0cec8251..249aa6caa 100644 --- a/symal/symal.cpp +++ b/symal/symal.cpp @@ -425,8 +425,6 @@ int main(int argc, char** argv) istream *inp = &std::cin; ostream *out = &std::cout; - //fstream inp(input,ios::in); - //fstream out(output,ios::out); if (input) { fstream *fin = new fstream(input,ios::in); @@ -434,7 +432,7 @@ int main(int argc, char** argv) cerr << "cannot open " << input << "\n"; exit(1); } - //inp = *fin; + inp = fin; } if (output) { @@ -443,7 +441,7 @@ int main(int argc, char** argv) cerr << "cannot open " << output << "\n"; exit(1); } - //out = *fout; + out = fout; } int a[MAX_M],b[MAX_N],m,n; @@ -508,5 +506,12 @@ int main(int argc, char** argv) for (int i=1; i<=MAX_N; i++) delete [] A[i]; delete [] A; + if (inp != &std::cin) { + delete inp; + } + if (out != &std::cout) { + delete inp; + } + exit(0); } From 21a93421dc0c9f55b48e208fb4f9f6f1bda06996 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 16 Apr 2015 19:03:57 +0700 Subject: [PATCH 20/53] Replace deprecated bzero() with memset(). The bzero() function is POSIX-specific and deprecated. The recommended replacement is memset(), which is in the standard C library. --- contrib/lmserver/examples/lmclient.cc | 2 +- moses/LM/Remote.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/lmserver/examples/lmclient.cc b/contrib/lmserver/examples/lmclient.cc index b26984df9..1439302dd 100644 --- a/contrib/lmserver/examples/lmclient.cc +++ b/contrib/lmserver/examples/lmclient.cc @@ -45,7 +45,7 @@ struct LMClient { exit(1); } - bzero((char *)&server, sizeof(server)); + memset(&server, '\0', sizeof(server)); bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length); server.sin_family = hp->h_addrtype; server.sin_port = htons(port); diff --git a/moses/LM/Remote.cpp b/moses/LM/Remote.cpp index af02a6617..7966fa929 100644 --- a/moses/LM/Remote.cpp +++ b/moses/LM/Remote.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -45,7 +46,7 @@ bool LanguageModelRemote::start(const std::string& host, int port) exit(1); } - bzero((char *)&server, sizeof(server)); + memset(&server, '\0', sizeof(server)); bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length); server.sin_family = hp->h_addrtype; server.sin_port = htons(port); From 6a4943ca41efd71d9039fc9491d202a78a81cc8f Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 16 Apr 2015 19:19:34 +0700 Subject: [PATCH 21/53] Replace deprecated bcopy() with memcpy(). The bcopy() function is POSIX-specific and deprecated. The recommended replacement (at least for non-overlapping source and destination ranges) is memcpy(), which is in the standard C library. Note that the source and destination parameters are in a different order between these two functions. --- contrib/lmserver/examples/lmclient.cc | 2 +- moses/LM/Remote.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/lmserver/examples/lmclient.cc b/contrib/lmserver/examples/lmclient.cc index 1439302dd..0d9fc23ff 100644 --- a/contrib/lmserver/examples/lmclient.cc +++ b/contrib/lmserver/examples/lmclient.cc @@ -46,7 +46,7 @@ struct LMClient { } memset(&server, '\0', sizeof(server)); - bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length); + memcpy((char *)&server.sin_addr, hp->h_addr, hp->h_length); server.sin_family = hp->h_addrtype; server.sin_port = htons(port); diff --git a/moses/LM/Remote.cpp b/moses/LM/Remote.cpp index 7966fa929..539809324 100644 --- a/moses/LM/Remote.cpp +++ b/moses/LM/Remote.cpp @@ -47,7 +47,7 @@ bool LanguageModelRemote::start(const std::string& host, int port) } memset(&server, '\0', sizeof(server)); - bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length); + memcpy((char *)&server.sin_addr, hp->h_addr, hp->h_length); server.sin_family = hp->h_addrtype; server.sin_port = htons(port); From 2180730a58abce6909eabf3aba4e22179ec1e23a Mon Sep 17 00:00:00 2001 From: Ales Tamchyna Date: Thu, 16 Apr 2015 15:28:23 +0200 Subject: [PATCH 22/53] check word alignment points in VW training to avoid cryptic segfaults --- moses/FF/VW/VW.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/moses/FF/VW/VW.h b/moses/FF/VW/VW.h index 6bdb1416c..dd9d0b858 100644 --- a/moses/FF/VW/VW.h +++ b/moses/FF/VW/VW.h @@ -86,6 +86,10 @@ struct VWTargetSentence { int src = it->first; int tgt = it->second; + if (src >= m_sourceConstraints.size() || tgt >= m_targetConstraints.size()) { + UTIL_THROW2("VW :: alignment point out of bounds: " << src << "-" << tgt); + } + m_sourceConstraints[src].Update(tgt); m_targetConstraints[tgt].Update(src); } From e45c41e665fd9b8a17136ba72b15130787956961 Mon Sep 17 00:00:00 2001 From: Barry Haddow Date: Fri, 17 Apr 2015 12:29:41 +0100 Subject: [PATCH 23/53] Testing of Viterbi decoding on hypergraph. --- mert/ForestRescoreTest.cpp | 102 ++++++++++++++++++++++++++++++++++++- mert/hgtest/0.gz | Bin 0 -> 148523 bytes 2 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 mert/hgtest/0.gz diff --git a/mert/ForestRescoreTest.cpp b/mert/ForestRescoreTest.cpp index 4b62e8317..23668ab20 100644 --- a/mert/ForestRescoreTest.cpp +++ b/mert/ForestRescoreTest.cpp @@ -1,6 +1,9 @@ #include +#include "util/tokenize_piece.hh" + #include "ForestRescore.h" +#include "MiraFeatureVector.h" #define BOOST_TEST_MODULE MertForestRescore #include @@ -10,8 +13,7 @@ using namespace std; using namespace MosesTuning; -BOOST_AUTO_TEST_CASE(viterbi_simple_lattice) -{ +BOOST_AUTO_TEST_CASE(viterbi_simple_lattice) { Vocab vocab; WordVec words; string wordStrings[] = @@ -242,5 +244,101 @@ BOOST_AUTO_TEST_CASE(viterbi_3branch_lattice) BOOST_CHECK_EQUAL(6, hopeHypo.bleuStats[8]); } +BOOST_AUTO_TEST_CASE(viterbi_full_hypergraph) { + Vocab vocab; + //References + ReferenceSet references; + references.AddLine(0,"in addition to EU support for businesses , also the administration of national business support will be concentrated in four Centres for Economic Development , Transport and Environment ( ELY Centres ) , starting from mid @-@ September .",vocab); + //Load the hypergraph + Graph graph(vocab); + util::scoped_fd fd(util::OpenReadOrThrow("mert/hgtest/0.gz")); + util::FilePiece file(fd.release()); + ReadGraph(file,graph); + + //prune + SparseVector weights; + weights.set("OpSequenceModel0_1",0.011187); + weights.set("OpSequenceModel0_2",-0.002797); + weights.set("OpSequenceModel0_3",0.002797); + weights.set("OpSequenceModel0_4",-0.000140); + weights.set("OpSequenceModel0_5",0.004195); + weights.set("Distortion0",0.041952); + weights.set("PhrasePenalty0",0.027968); + weights.set("WordPenalty0",-0.139841); + weights.set("UnknownWordPenalty0",1.000000); + weights.set("LM0",0.069920); + weights.set("LexicalReordering0_1",0.041952); + weights.set("LexicalReordering0_2",0.041952); + weights.set("LexicalReordering0_3",0.041952); + weights.set("LexicalReordering0_4",0.041952); + weights.set("LexicalReordering0_5",0.041952); + weights.set("LexicalReordering0_6",0.041952); + weights.set("LexicalReordering0_7",0.041952); + weights.set("LexicalReordering0_8",0.041952); + weights.set("TranslationModel0_1",0.027968); + weights.set("TranslationModel0_2",0.027968); + weights.set("TranslationModel0_3",0.027968); + weights.set("TranslationModel0_4",0.027968); + weights.set("TranslationModel0_5",0.027968); + weights.set("TranslationModel0_6",0.027968); + weights.set("TranslationModel0_7",0.027968); + weights.set("TranslationModel0_8",0.027968); + weights.set("TranslationModel0_9",0.027968); + weights.set("TranslationModel0_10",0.027968); + weights.set("TranslationModel0_11",0.027968); + weights.set("TranslationModel0_12",0.027968); + weights.set("TranslationModel0_13",0.027968); + size_t edgeCount = 500; + boost::shared_ptr prunedGraph; + prunedGraph.reset(new Graph(vocab)); + graph.Prune(prunedGraph.get(), weights, edgeCount); + + vector bg(9); + HgHypothesis bestHypo; + //best hypothesis + Viterbi(*prunedGraph, weights, 0, references, 0, bg, &bestHypo); + //check output as expected + string expectedStr = " the EU matters , but also the national matters management focus since mid @-@ September four ely @-@ centre . "; + util::TokenIter expected(expectedStr, util::SingleCharacter(' ')); + for (size_t i = 0; i < bestHypo.text.size(); ++i) { + //cerr << bestHypo.text[i]->first << " "; + BOOST_CHECK_EQUAL(*expected,bestHypo.text[i]->first); + ++expected; + } + BOOST_CHECK(!expected); + //cerr << endl; + //check scores + BOOST_CHECK_CLOSE(-80.062,bestHypo.featureVector.get("OpSequenceModel0_1"), 0.001); + BOOST_CHECK_CLOSE(2,bestHypo.featureVector.get("OpSequenceModel0_2"), 0.001); + BOOST_CHECK_CLOSE(2,bestHypo.featureVector.get("OpSequenceModel0_3"), 0.001); + BOOST_CHECK_CLOSE(3,bestHypo.featureVector.get("OpSequenceModel0_4"), 0.001); + BOOST_CHECK_CLOSE(0,bestHypo.featureVector.get("OpSequenceModel0_5"), 0.001); + BOOST_CHECK_CLOSE(-6,bestHypo.featureVector.get("Distortion0"), 0.001); + BOOST_CHECK_CLOSE(14,bestHypo.featureVector.get("PhrasePenalty0"), 0.001); + BOOST_CHECK_CLOSE(-20,bestHypo.featureVector.get("WordPenalty0"), 0.001); + BOOST_CHECK_CLOSE(-100,bestHypo.featureVector.get("UnknownWordPenalty0"), 0.001); + BOOST_CHECK_CLOSE(-126.616,bestHypo.featureVector.get("LM0"), 0.001); + BOOST_CHECK_CLOSE(-5.2238,bestHypo.featureVector.get("LexicalReordering0_1"), 0.001); + BOOST_CHECK_CLOSE(-0.29515,bestHypo.featureVector.get("LexicalReordering0_2"), 0.001); + BOOST_CHECK_CLOSE(0,bestHypo.featureVector.get("LexicalReordering0_3"), 0.001); + BOOST_CHECK_CLOSE(-0.470004,bestHypo.featureVector.get("LexicalReordering0_4"), 0.001); + BOOST_CHECK_CLOSE(-9.28267,bestHypo.featureVector.get("LexicalReordering0_5"), 0.001); + BOOST_CHECK_CLOSE(-0.470004,bestHypo.featureVector.get("LexicalReordering0_6"), 0.001); + BOOST_CHECK_CLOSE(0,bestHypo.featureVector.get("LexicalReordering0_7"), 0.001); + BOOST_CHECK_CLOSE(-0.402678,bestHypo.featureVector.get("LexicalReordering0_8"), 0.001); + BOOST_CHECK_CLOSE(-54.3119,bestHypo.featureVector.get("TranslationModel0_1"), 0.001); + BOOST_CHECK_CLOSE(-62.2619,bestHypo.featureVector.get("TranslationModel0_2"), 0.001); + BOOST_CHECK_CLOSE(-23.8782,bestHypo.featureVector.get("TranslationModel0_3"), 0.001); + BOOST_CHECK_CLOSE(-25.1626,bestHypo.featureVector.get("TranslationModel0_4"), 0.001); + BOOST_CHECK_CLOSE(12.9986,bestHypo.featureVector.get("TranslationModel0_5"), 0.001); + BOOST_CHECK_CLOSE(3.99959,bestHypo.featureVector.get("TranslationModel0_6"), 0.001); + BOOST_CHECK_CLOSE(1.99979,bestHypo.featureVector.get("TranslationModel0_7"), 0.001); + BOOST_CHECK_CLOSE(1.99979,bestHypo.featureVector.get("TranslationModel0_8"), 0.001); + BOOST_CHECK_CLOSE(0,bestHypo.featureVector.get("TranslationModel0_9"), 0.001); + BOOST_CHECK_CLOSE(0,bestHypo.featureVector.get("TranslationModel0_10"), 0.001); + BOOST_CHECK_CLOSE(0,bestHypo.featureVector.get("TranslationModel0_11"), 0.001); + BOOST_CHECK_CLOSE(0.999896,bestHypo.featureVector.get("TranslationModel0_12"), 0.001); + BOOST_CHECK_CLOSE(7.99917,bestHypo.featureVector.get("TranslationModel0_13"), 0.001); +} diff --git a/mert/hgtest/0.gz b/mert/hgtest/0.gz new file mode 100644 index 0000000000000000000000000000000000000000..012f9efbe608cd7ffd0c0dd5a4aed8eb3bc526ea GIT binary patch literal 148523 zcmV)JK)b&miwFP!00000|Lncnj^xO-rg|NNgHe)|0B``>^0{Oj<=uiyUu%bz~~=1<@L$LC)@|M^7-=|5Bm42g_rHFAz4BlG?Z1EifBydY>z_XVmvH@W z><|36zmc8P&UyKGQTZ20f-nw+?%Ca&qG+uvND@h^Y< z<xfBrAwp`v5bYR5nP z=g;lGj6ygu8QCOMs>m(#vO50_P=$_1rP7&@v7>t8YQ&l#MS=o zdheY##6>f8(N4Xc$@l6fe-Ui*DxbV6Ca=o(xAXho{oz0U=0E#W+{gd1e#mIetw|T#KA%R_HkQhmK33)%xdo>T0>; ztU70`^VE?Pub%~Bq=+5o@I+^+S00WtQ-b?N$2s~FANn}w9o=yF|LI@v&;I>smT#x| zpZ@;Kw}1Qm>1+H*n@%d{tZwv3yN+vVorxZ4XSCJieYC|RtvhZQgjuTAGmR2s1RE9SNqR0qg263Pc<1}&)=N;gDyu0{pRc6 z{r;c-@I&m}yZ+qsl)RekB(p-4mF({#eD%Az4tI0UnOjo&3g0K(>m4lxwmkO%N6K-@ z1Tt$49sHzZ`D5bkrh>-qPW!d`?JuW&;{Gl=XL#w*e8Y2YIxf8zi%Q)6-B7Xc{D#gs z@@;jW^U=+I{*ZH)7@nIhXgA~SUMHPXC%s^J?j7rsQYR7et_w#bpVFz5{_A~KPRt(s(IHQ2S2G=RoGaew37=_i z38GvZp~!n?iznQ7TrguJcXHneA&sT{{G9WSgFv*H}5 zewloKJ@?@_E>-lS1FjO=WK7U(j2p6mUhAEG%{H07<@!AOU3DRd_GGzi_QoE5>*0X2 z$PpKv>>J-PZE|~KAN9q|-sguiEwXOpmv*=>UuTcH4KHCm9je!Q)N@3<2q@D5X7OyC z74|&MGU2QnE_|e{J}2T8cUneT;&g}xmDVV$XNmX)qP^GsU%p1qzDb@~u^m@IpOHhQ z4Ji59i510!RHtdE34d)Zl#VEQ*9jxEjArTjikhR2FY4-vS-O!-ABlJ(5C2$bADwjj zewUvX^Hv!XeyRiA;WNZMSDhBd#0u;EIp<2r(+r!6@aJ}dteg+^HtxLk(Z)F^q$kyZ zdhJxQo@|yq={X;%`M10Em#@zW{LwLY38QL3xVgT4rrBHh%&3}h*0|FI?6Mc)JZ2AJ zI?hF(-5`&6_^opxNb?A^&TMrnpDwBU_X8fj)6|VzS*zG?nfJYK@U2_0n>8thhhu;?f}ZX-=?n zt|Qs^T5slg=Un$K+!K>7()81NCpqW6*^@Plx+4#FbL)iTk@+>%G3l9#%&!H(?gb+g zb~xw#gXeDUsRutiC?2N^eqVK)yvOShfmwa50rk9eybRiv&bqyp-juieH0AaF9?zAd z&hIenqJ*@uvoAYmtt4HghUQq!T?m?R_9xTvNmFhK8TzT){-vev)AzYNoz-46d)rri zQ?Q*HaFwplJ)P$0GtZr&D(AC7yy~P6QF=5_XT1^%L454C-&fE+eP3Ayj1Ikg5icM0 ztPP}>4SUAf&i8#pv9w({0o-^xtGDHe?=uO8Gfkofwyw<_`A0t*cJpQLM|1o2cX9&$ z{uz+01A+VJ$Y~b5 zT4Jyf8ZMGy=+4zcJ%1uUV2$*eEao>r3EkK7nI=yZfBoP~zki{xm|_TjZmu+0k9!Rg zwBuxcPriyv=2WE87j8E$yr)IE!BbFzF6QqbB1TluZ&dj7_0Ln{!5J3**o|UAc1$Sc z;`M*7wXwUW4vJx+Z|RbZoO~a!Agoq#b1cS%Ad0a4; z1jDx}wIIU%!|mp@vAI_&AUIZ{8rMMOrhS=cSYHDFfFHDf2|7eK*EVN1j?t>7Xu*5Azf->2K6-W>n>3bsq z>Wb;vg|&|QOu}J0xiAb1$RDS>4@B?w)=z@AN(p0?ckv1&>z8ts$S!#1B3X|uDZx4! zA_Wum*tJR+((gmQc4}zDBOQj}xtdD>fBbc3QU}GIm`>FnN!Y#sP#@CYA;4s>R;Ht3%0&f9uAD% z{#M|diOj8Hk~)!1obn)5jFPnFR^fYjkxV{L`EfG&&vXh-aY6J2KEZzc@bjnd-#`EI z>*Q*zzNFh~H19htRqrz#gM;ys)0a1f15;Aa6APQJ#^T}c%^YdsF&J+5n=@h-AF|`j z8NN?PA)sL~f6g^;#F*&!si zip1F-*oaIp02?)9WwF-$h_!~};nw%MsC-??GuZEeutoi@&JvWud2tq~b} z0yJizu~|lgXY*ljG8a0H55h>-Y%n4^>nx+^9t*1qYZ>3Y)d8yFz^WprKGIJ`^olM}6wZNq5pj8|T~`1bm1AQnhjAxtF4Yp57J*2k)qb9= z`=f#@6HX;i0aVn0iUoaE*#}=hBUG&sr15n976<z-w)A?C*~~>DG*6U%~yJbXmzg`|MOs=qMoL~xP>QnN^@)n)m>xsdeS z2V?^HK=6S;L6)qzM}!(&UwB$=QVMNUtJW{!NT_hLu5Xxvcic!$+sT3v=VU~Fv2vM$ zP=j5JP-!EYN~(sZCYWSGRM)c9dpvUQ4mmXeIxJ5#+!qVqcE{YG{{0#rN6-*2Drkx{ zXc+I9ZgrCk?{~#`DdJA?#o`X>t)?xmK#fySWB;xNYNq>RTGK)Wlz2b)r@_S3)#oVD z|8~68oHB+(=usRKD|aoB2cM^nS`H__)wERa#26l41I9ogTHh0^BpF7 zm#W){=8BCpx4G7_!K`pBzFMXjHng&eUW^5702>i(^k@CsPn%$5l$jVD=XhGRrXCuO-Di?NSg$q^OuEk6(e8_UUO*;R?1p0%C5ZJGDpEX$DDR2%29;3 zBox<_@qKcRf;#oEjoKwgk=e);84o_Q4is&cg8N-pm7=&h!G2JzK(QK+`Za^ z{e52dr5G_|Xs1f%#Nm3XGB_bvXPITueOhmvI8RSi5+|r#*r}Y5;KWLtNNTYfktB~2 zCY=(;YtGJ56E=LvxTjBJSv66nJ9~3VKuzqUCKmQ;KcJ%D%ax9bqb=vkK}86wg{U*| zD6*X^2^EXdmJk%{qarQRcz93_D!9x*1(WVry_cD)LU_hzVviE2P?A|Et3(AQX*xM-x7f_4GNth zCl0q2BPW%~I~hLRGS;eDP)?NDih)#St)QSYm){@q5D7~#-YTo9xVThH&5LF^ZT}L1Q$Aj#Ny{EyhT1t~i?2bzad_3#oX_hyu7+8y8R-3vsa* z8+r$Pu;GRcHKr?y*x(}yshAC`Wh3VH>LARhJj^JvTn7b@TG=q)TeZS?X&OB;l*RE3 z^z)R3k>2nbdKBrbs5j~WJsOTWM8>#B(`@Lwt`v_i*8m%kz^sG~^9jrq$Z#oh*o)6y zDkUngvD#EY2<5aOF;JEgWz6nCiCQVKL2e>uZg(Iw^~8Q)|Turol=r{>imoT!yvep%LEAq0!A9zQEMs1v-7wg5O{;D{aR5m2UaaC_{N3^s3590~L4ch!_PhFoG5 z{~}&Ksx+RoDn!5&k|$Iu6GCKfA+oXk_U*qvqSSO{rDk{57lX9glHEZS{N%P76~!KEm?YL6dy)=>nT+s`VT|+sicoI zeT#q(?^V>(Uogi6^~%5pfsazjj~^0qA36ma?S?9fa~ZYue!~Vym6owV>56x3L`t&2 z#_`K4KSVVKA2fzLv&P_^6H-Mjd(R4sX^rs=EY|Pe04z3z#ZXB(ubK#e;8N&VgV$LB zv7MR#Af7LzP|*UJuGq7VqQy{0VP}>bGJM7vljPEl3TU!>yC`ClqsWbfoW_jLcbeFc zXU2o*tWv@Q55PldJXD+Ud>lx5R~W+vb(Ivaw2BbKD+>@pJr@XBA0fLJSo83qUi51| z^pAi4J_Qgb`!ar`0MY%{SLU(%Z3=b3;Nn9&UetLYvAh;SsBU?SeZbxw8h3?iT|st-YC zt$7|fA(A-34`uzEm#W}edqg37NeOUbiy_y?V1y26w(ZW)mnqkCE02LF@d7M}lAG56 zQMS!%lpKD&opU|85DC;!u|Zd0145)KY?K{;9SIN9FDiJQLYZNRwZ5P0IuJuLtVH`( z1nHx0fZ@U3SvuBfk|E)iVU!RN$DTk5Z?%pN%X97=Nf^~%RosW`V926i=J8=ajKY+M zl_zAt^T-J*;K>Vb_yc9$X6VmM7Tsyh9DNEwmIzs>6$K#6eqpWP+DE| z9NwOjky7c2ff5~>9e@%s=6C~2#Pl3MIigfqEjRn-7zhD1X4}@{+Otjhm4L$eHNBUByJ~dlhXDs(JE@Hrz zN47-lkKWi45iwxPYHZ2K!m#_N<>4odHdNs{Mx58Q@2QF7JI$@}@us_hIK@^>6c|Px zh!b&3T0zZFS3#URZmdO|l3GS2lSMa5mJlZyr6z0+&Z%X#BMY=lC2>+h_e%zCD7yI5 z>lW8Zq56*r=&}}F5Gn0Wm&Z&lX@ju0c+tstIh2WP7GAVAT0y)(yc`%W^(A@Vib;mO z$V*4esbvg;qy zNw_(bmqK28MLWymT`9l~aMPe(StB`nDf5Vn=hMj}9|#wMB$Rgw615EqmuE`I2}-4J zc{{mM3Ga^c__{Li1(}s|^JR`D@gBH~l#wr7+%6~@h!c1_&`g_;LQLn7}Pm&FR7i*<6E`D`w>@01p zU1*3KVbSk?|IdHumCXd4zN?ujqMPgYR#9M4&!X@WhDk%7Td*j?qUh?y1Qww%{k#N< zpeesUW)Z#CfmPILiU8L8f&l=Fbxs;CYX#Qsoiwk&64cdH@kCkR0a#Corv)BU$B<4Q zBzh;2l{&DfQW6!CPEU~tVchm%T;fF2F>oZZam3rjk?>OT`1Ce#^oXNKu`Ed#0Y{rf zayxC2ToG2|bzn%WKPfTo?R zb3hX^;%~2#oDr9KS}Gi8=UW9<`1B~{ZG6S|DS`D=;0IuBpNDFvoj@Tc{@zYtf>w}V z(OxV<5(?sp5pcTng@1ehMps_t1$&M2O7+WXku*mQ54@^?SD(KAnE;jqux@(C9g`mI zPYCt^tWALxnXH?F*fpKb0j&3Qx@3;CxY;6r1%(A*ZI({|a_>@2@#-?Er(V=$tq$0Q zma2v9n(k6SuX!PM9Y9H?^iXAof-TqmBojuW|9mfH1v<4zrx_BZ%czekZ>|^(o`6$M zo(kRQyTd6d#-r&}fmT9a!i`q4N_!3*Aq!P2jy9jw+dU6{$t&p>&R!j$t}u#%NOxQ_ z8>IGuK$NuRtgo7MY6NbYK@p} znJg;Urb#={-OC+ffphQbl+2e_S2zd0Xr?^)lEWA3YARo#b5`L?*~%KNcpe8)sI~D7 zs^&Mo6M``p@A@uFo!x8vZ`6q_R9r#TfI6E{r#?;NL6?4aqUDm;N|#ca<{7#~ayOvM zI&>*wv9uI1A_Fn1S;PpZoQtn40%9H!6G;z4z#J#R0G>VP^)dt<2@HwSfo<2x;-V6_HVby5df2h>T>IuqMt&5L}UNk4Q z!XSGcq5L5UGE6dU?2#ZNvmzlarJCra35ew+gPMel=z2?7BVv%58s_GmbVhhZLq>(O zr8Vh10mw{2rjCUdgwu_j>93KGp^~(q&X53RoN*sFG6i)WsS_~+1a&r)HX|*QvWLEO zQur22(dkP9P5tyG(9~DNo%UuTHQb|#yxe4M>J3d2f~XoarL(AdIpo2d*WSNyil&?< zPAP|;VGiY01Lmw7gjF({xd=QW3p}P-@CYTjo@oCAcpktLaTbNx*(na&*7kU{`OOC` zjcrCLENNirbpXpN$KtypfF)q5K0S2Uq_jd5_S%)~jkgmWJfIG!a||(5Tc`2^mL8MT z3G0>5OD1W!6*D<$kO?{IipX@pa#@5-DT|cPiYC`d$%L4x22kxwtlX+5p05Ns)$Vo4CshKjtH7AE!gfdi3&_zYH$edawvsf$SNsS8bQ<; z$h=(2i{#oZx(O|mK@$?x5;(M!Lu2NoOQZ3M(euh7 z4D&DIG6);RjSUBmhu_X#xViqUPvGYI&k0GQ&Izd|azsjSLGR0H5;r`2W67nbs33-% znifWqqw>Q#V`n56)DXVv-;-dD2?|F5PXFMCwjbie^N0SPS?x1s3kkLUdk@gLd%XwF z-8WFrQNy)M4KBq*-}FhTL0!I~26gg5jXI;HXL_V5-koLlE8gH`b64kSmJ!ca3)7&q5{_xpeTL*UG; zSJ0_hBsw7jY$l52U{Pw394soUeX48aQ4iAU(J&|lSR)6alLL!|5MpY@MNwdt=soO# z6-g^6i6_b(?HyQDCT*{2`rV~=&Qtb(;<$D*`$v{b{rrRb=r5UPssNR82 zPidxvsr)23Wp!mD2L(8-Sjz*PN~OnXIu+MSm#9v@nAhQzE114dScsTT3foUV9f(Ug z#*e%cLK{0Jnz&Tw@eq-nG)$7@8Mq|1VU}v39Js_8^CWpx|4A}NGlfaHWzCnz*%i_Q z==--vIn)oSfH?zmxZ`ZHuL;Z>2lFrjYyBS3mNDFDH#8h_? zpp*wCq=mMKz7C&?zLdgLZ)&abL{ZDJ7(`Jvf2PyY$o?Eh6z#J~D4K(oP(c?8Y3%}K zL7{yzLZHyGDU{l=m@bR-3+E61w$n@!Tskj~#H5_&#qa?!g7Jw(L&h=MZ@Wn*iOw>| z6h)oc&T83t?gDsdCfUwe$t)HPonanrjG=KSW5nI|%p?jYo;zniqB|04>gwzS+8c=? zE6+fp)zncVqx`A38hP8E8&}FJBWuM~0c+a?TtPVPESwHzZJR8cJUH~;X5{<*rj>H2 zPT4fgA;_lf1GrjRkJf;Z$b}Ja6-MEcp}7;}!3B&WFp7-ALKKya;i_v$ni!N#j^A_! zs<^klW!+>g<7UE$4XDaYHvv_B|-RnrXw3YqZhh`(goNbUpVTb2I$d3xQO3d-R~D9ZG*?AuQ#3U^*=aVi(50OqA@m4s3(<|U^)Ev0rS+|}=8 zIUpqIU#UfG4`i1Y&2eIIOIj$r3D0Y#O+=GfBphajs=O#GEo} zqCNYRSVDTJS}g7FT(Xfi>a9z%Ak?rn0)%$0jG;)695tH?+j8_#>tP$SMgvW&g>BGB zE2F8EqWz_9$X&+Jkg-PMXd!_nBQzwA4wN`rPx)l+q@;IHo?8hC&X^`a0}i2b1`g$B zr-^a1D28(S8IiCChEPYV21DyllI^K~6ymU}{t35ijBh~yR1Kc3r^eP|PdxwQUo5q0 zdlEjjQlZ(b!O~-lotWZ}2e0*oPXP&xx zMEFF1-URsE9Pq=KWhdZi-Y#3;<4ixwqQ~{yr^b`)w2;!rbJW1oBc38oX>WLtd@^KU*m;a< z-xx-<3PEDkCb6oTP+LV{JuK=zIh?q6(jop^4^9wR+rz2WQMOeG^Tbmrsr}H{6Nk4O7A&-Nv&E|SlTM%XB5_X zj3SfxHJ!o&zt&Y)F_3Mt);m>PJ@924we`5?8?*wg5D%886%y`=3HP`D2FG3`wPxwm z`7K0`P+yhstmbtlbE&PAPS2$R>|ys(3b1^`z%trsB;@z17E%*^0M;E*4hXD-k(i~t z%I?rYz=(hm>G`U8wf`v1c8cl}4)PaqX?LF?lyc74LlH`(6H0Z?X~p7meM)gB&jTpm zY0o56G@v*O_p5-Jh|j?^p=2PGP?q|}Ak-Z}NJ8<4hh-|N*~W&M6#5%~9?^COvim-T z6to;FSj$6;)na1Lz&X((r_r(~SbIZDEBSVkjZ6)7%L+PA%-cV8nyjhbsjrPqh>&d(t^c9p#dG_WuSvbN2IO|bj+Y5YxhSi9ej_Fv~=)T zw9LVZO7&iWB2Et!$*s}xzz13cK&e`b(7uaP@?-@iatUZm-*2bY97MwBhKIlXp|U3# z5D}d>;gdh3N=iUPaxV=TXaOQkLgX&J!Jb6RmSN5aB(^X0XfqEIp}7!;>f6l#iR_g2 z(v$5hWkDi@H;UXT1dy~1lH}pYr(zkJTZs`)r1%_V(lz?Ar{|4k%JTRyHh2Tx)W(}7 z>68j+b5S~O2$|&R^Dd|XfjkEz&jV&w%+ih^l4AET0*fEOqLGV1E(TyZ7Ff0okDf-0 zX9HT?>7%8T>`aN`GomG+C4!dn*_jA$9zv!#e)xbN{^PW#L%8!Rp^nv}n?)ZMPLn22 z`J1PM1w8@vRfBM_s{~SJbak%I%L`O!-X5pfw z^K=|Cz=b(w%>F|}<#M{=ldq%BnVl3&nMiyjds}xaBXH?|(>?@!fJ+Su<@IrEXcTqP zD1ZrI0+>_+lLIN05sF55(J$)dqj5ie`03l9{{CxEmv3LEMxKQ<-jbn^oB#};08IQ^ z1Tepmd4$QBdFHG$fiaFe%6#&$K1Y~vmW`KOkyFhBVyx5L?4Hlnon}JI$k4OythW7# zIEk1LNO`U`^~$P$t`fGdIo5-ixY6adh`BlBam4U@#85-e<9zP&A?UFRdVJKkudyWW z9HqXG-tt7wcli3|;RBb<%SX@hcttKYZ;4lto|$sI;vLtP^W96^EAT4f0|Q>Q&a0(0 z@c<-aGJs@_AS1L!fTX;zu~jK01(f3|1jh#i5(LzyK&k^iODp05KF$L^ckqc!ZbJ=0 z4FNvu!sn7kGD8~4B8`MJ5>(I;K~!&8M%L|Sa!BJn3PcXcWx@(MB&a5+Ccx_LA0Mcc zmQ*4KLHh)fMhsVX40qrJ5@>XS7%q1NJ(^8ID!cMjcC)&OYGeY5?m(~?iAeH{@wBc6 zx}=f@+poT$HU%HoDJ8TZKv6M#vRWOzWZ|^!&Yl2B>%;H1G8M#mK!omxa7g(0fNI8c zWWh_n$$}a3G^_rv;te;NTA#Nee4;N~ve)tEycV8W->Ysm&lpaQu7ff0tIsl~l)kwz zhMLbxWlW@>8jLw7V=kJ;4VuQy^M<>ipqu55W?s?7r?Q%+Oi^qNO#|L+&6`E@8@n-R zrDkeisXi=~mO;SMp|G^6htCc@)EBAb2dSiy{;4rEt2_M@G35Z08VJo+$~8|_y^ss4 z4>)y;AOL4i;2eW6_i~0aHs~3P7^}t@tM1ef@Uk;r_RjTWAZMkHXF#Yv2tnfjLh+!g zbfRY?S;Plfq<;MH^QW(${_^?f&tJcP`1aG3wV5!2dots5ZR*<`PS@|WMYkb0p2cif zH>6Y0K-MrxhJKfnkEYkFZ%HTF^(#qY=4Pt%oQ0p7hEo8Wv9t^QfMdbKd!n8Tb2IuV z&omj*Y#CRdzViq4BtS*{aO1!CyWjuwAI6Km9^v=Sv7hdKOtOHci08%J8^_kjU)7fw)R%<% zx>sL@Jm*kfU{_7*Yn<#!Q)6Y-*Aq(XAppHfFAM2)C%r_4_=5C08NQA!y|ytMx#})? z?E{~HmFuJs{hF8~!73vUuR6}PwUM2P8^VfrK}&I2TQSggUP$4hX?U%bAg69$ZTy9j zWH;vUhWmM4L(Sy22dntR?tX3H6Tr&P#TRG;pQn-smG-4lsp{YgRHEb;s8qfYPzsGiXOGX(%60u;(PIWBc(oy&ra^52RXCna{@2s+YeU}7NIITu; z@|D{IQ~c`EoOixfS~C`-GT#YqCb!i(GKHibFg5Olnjn+#O529X)H`F!{f#4M$yBBq z_eQ2@H4bE|fJ}8zXvH2UbE&nZA<6P^Hpq=6Eg*^W&Nye){v86dOe&K~f?7HNllE6j z+t$}WqIVMQZ}2}uqEZPZkm!gc+H?wUqner}y1q}IJ~F;G3>8ivAxNtSL)$8#)ExCg z=r&(Sdq^Hp$s&tn(M__b!Zdc#WD(?0RUE2-IV-kdyGwHs0y3nApo9)VpbC`GdJRS3 z4iZ67KJ{~_Xd-B%CL;gBgZL3WguZ|O{L8O#0{OmO%}cVqI!=b~%rm~f z>1{Zzo?AV!;L$j3$!BkD2q%I#_7MX4PI9G5#tglU;iF{?wLIz75H38GQ+<)o5h(i1 zY6*06YF8@MU!VIZSMGfU*32`{n`&Q(!v_moYm$f~h0(MHWeddVH4js~KIE`Y8?WW= z*`5n>sLMc3Ey$^+4*x`x;*e7M_5i7mRwZ#sNYk%MZ{HV2bNp5|Fgj&Z$;`)K6Inr%Ftn&4p&Ayt z+PwegqhTOQ#YEAHDf;KCohVQ^=OoHiu)O-nre$^n_sYn2@nWR)a{nUwYyeekVhV%*%koF(!IU?qi2V)q>kfh(707@oV9GR8uA)W04~8ZH#yQ?@+Lv|x{x8~Nr>trz zwwDpArFz0tQ;u3~EEF6XI8=dtI_O-W34IUZ1`3gV2CIg_)TbB%n1_n=D?L8oZD*?+Bl-I*QKunN#;E_!;*rbNhbM;szh zni$bakY5-1qSI>Nr{G70HSn@+dr&X+sF#h}_SOrYCKJG~u7GJ;1^qT?jfi>^U zsM&2I+R%q4;d+~dQRY4|VD6|;VTd%=D>vqSXq!R%9xB$A@ub!Q(`wdzpbnb#pknH# zPJ_mM+`n-4@=>}UKZNi8p?|3lUx#1Qr@u|n$SWtTqiDpURFMhR3fs*p)WG~aQsMQJV&Fct9qJM+nR_mdEGzU>RqG+LxP7ozRC0L5_Q4A{pr7*eRugp1}@O z(E2Lqq4xAofB)s%zkUAn^;ql?h`?SC<|XwbrI3+!9k2(IrwaCL6<&o`y5-l3WD?nA zQaYDR;o!DRCatWeC$>*aCgg^yCDVF|pbQR;z%x^kPvVdPhh7gUI8>rIMgapHx`V}; za?~8BR#MZJ7fi)v~O*Yk&(TEwpBv~2RoT-VK} zZNW1Upru$YrzWo@WYX(7y`acSZ86!AgCfYJ^P*^soi{Xn`I=9n>`@Pt(auLA%$QY6 zqNM1IMp1DUgw*xh!MrqBGbII*MMxIa%%0U6tn+fwBi`g>e|W$5Mw=f~L8CjSHRI&A zidsPbWhjvOwO+6g* z&L|o*f!WGfK|S|5&_pk~sh2$#Xv7@^o-y^fe@-k!n*$SNnD(m}1!a%^h*2zw0ZR^8 z0+xWK_%8xjdjHpWxRw&1MM)%IWFQg$F#$rhH^5?YHwayS<|iPO;dtgdrG$xj(3gwB z!s+2gF|wa>O9r{ld0}NF|6@DJ0-+{~V%kX=V-U09b42C1H)K%s;q|rTTl>6~Rc;N)vn9fV57`fxGfgG>vq!&E46{k<}I__wjpRG&W!Wl(2 zAZ|$;-B~aCKQ~F6@L}Lwst9euU&A}YBPrg%BCD)je@Z=ivDy;%+k0J{;+<%x88zHZ z<#eB|9$mA)J)xqwF_%nk8g6vwm}bXnsmZ{N4+n_x*p|O?xG5!WR=`a}+`Qq2>c#f}S*^HkHDwc6Sp$Q&bcd_=;mk~!WAFp^!yoe)+btb&AT z0x;W!RhwaG7zQ``6+;v9#w*L6KN5X1y8cAxT<@h3rH_-w(;G%NrjO%Fh~4Sq`jx82 zMd%a6ps~tR0!H>XDr2RwRL9_})daUjt#36Ey?6}zJYt2Q2G#X6z3j}8#35<rY9Yht7Gf`H|R^)p=+cS2TS1fCwY|cYhYi zBs*)Fv4Un3@3dvKA%^TfD#A(5sm`B)O;&jA(HE4+rUhmDl!)#9XZ!s>|KU?2l;*NS zq+ifFDQz(7!l;-LVKGLP(obL%7&XDDbyM0yYf&16v=Z!C>qWEr8?CY4R0p9_$!tKV zb_mrdik@jT8mxf&6gMOh!Zy(8U^J>RY}+>F4NP4y73%;%)n2)@PK8 z*jB()#3uxpIwGc4Dz3#i)EC>5IHY^u9KmF(911Vt!s)1vL!}}%aHw_;RUwUX>T3}O zUE?=*skb41L;QyL?QlS5T9c zDP|d@J86s))N=<0c_y8PF>NJ1NVOH-{`e2N@Rot8W;>KFm?ER2l9(zLwY_1A0ueBE zLQJh#c%wMf7v2&%WPNyIs~y{cLqgiPXBthid0g71AiT@J2A`62B>SDZC+m zL;SWskkGq*C}Ff~;f<0`ec=ruTyh9kVvA#=Ask560;v`j-pDAi0fM&0?B}bw+E&NFFpAy(u4A z-k30X%e~<&qKS0BBHb8(C>KPTZrf9wA!yVyW9*`vQE$8D;|NXshx^O3^|wGJx&Q}U zjc^r9HWkBFxe)GYTt##fa5aOg#{>U;I>$Wr;ije048bTgJs|86O?NCJ;rRfd92`Hu z@wRN2LoXHJs@!`8Iuii)an-R%2BBvx8@F{ z=xYPmfdfYxn6p;>uXAk3^}wX=2H|P%#UZ@ExS_f;{kz z#)(!-;i;<$O$~|#5JlX?RbZGfikdYBrS4F2fc~ltCi%!p;w2ZK0xGFY`1G|4Dj{^= z6I30jO35jJs%}tK0j1m-n#q0!$fY;A6!U>9Mxk<9DNv|x3LSQ$cruDpw-rBN2~ph6 zSfZW_EZuRUCM@kb&qoJx27%mNAQ#JvBaq8yRdkF7tU6>Mw{VPx1ka_b03r#o8${Bv zz*B`tS~yBP8NTUD5$;QxxwsZy@MZ^#!%GAL$awTN%-O2bL~kOzVaCitFj>!xq}+gq zO`5`(a3TSw2Eg=W4X%gOm#=qkkz70OfT=r7#r0DmOcmUL@GPdH3x9#B<6&yW-1nEr z8oNXe1yyf!!*QGuPWpHPshC_Uwg3Sfs+vPpNTb|N0D?i+_|06(ZxBX^-}VO*I=v4i zj5e#jrG{{Q4jiOH2nSMuR6C}?$tcbsihGaZRMemVQ5;ZI8H#EVNI8mV9Jtv_;qBkQ zeE(}wPk;!=P&K>=K?rU+iJ4(YS8LTDowI~)fy&xQ!Ix0T1QDl&i|+Mx+Di!v9ZC@O zJn2!f)B-9i0YyY^ebPG_RfdP(Ys;Guj18KwK2(+Jn*^%rMpdO)%1MOZHUU5~-AX1= z8;8oZQG!EtbEvdrTB$K$9Lu>&5e{HNEVm7q#3Y!|7jSSe?KT#7oBc*OcN@;deZ>*Z ztt+EItK)}r3o_!jJpd3&w?Zk_uY?E==&1}nl?tT=eF5Vj&fiSJrTONX&N8Ldq5J{z zjK4#;#CUworVv(05)w^@aiZhGbH1oCz;w(yDI#H{Mi4u^#>#Bh)a+)?4uT8OA)>>`FQPudRr6lERKc~{T07ti@gg)={1;Il zzLMj^0$c5N#n;=w9zw>OkTKT#iI5TM>+~Vx2wp4gw3~_&`3n!6@;@d3O7;3mm_zBS z>p#x}%6BTfyQsx)_$*1)AGre2p|-}A*tsc~SBv<>I?cHzud4nkv#g&+0-tC;k(r}O z`PDOMwwpnapi0b%54d`eI9HB{7x9`RPvVFVA8FYrM_k~DlPW%ISBxXYcK3oKN7Xrx zFGY1ftv#(0ouo71%Nt*G%>1MnU&?i0f-j}{atDP>^%6(K9a;$el3wT^s}l+UvBy7Vcdsx}L+E4EVB(mxPBaP= zAHEXSRS1((Q?!6dX=(CY`}KD5&k6=d4$QeSCq4^{a0tw)E!`3uNUJv`GZsC%kDy#? z1m(Hmt>$BVXOtB(?rBs^^eCa6lK>C^G!LLXfHQvDf)wzTKc?r8Pc=c;3~|1z(dC=3FMDaDM6yE~C`l3ZM3Rah%AY8MNTyR< z@EX%Vjw?-*5<~)#b|=!MEpR17UGhRUDK8WtBn=mbaw^J#kW^Z;*sxd0u*`<5+-&-6@e20)19W3eBQuhACH4 zrC6VR^ngpvaIsu6dbu{>(j;7V>e(R(v0xtas#AQgQ*7d}0vA%NR#DGgJCy&2>djQf$n^-{S!dliYgADiF#CLC$D_|KRExHE5pc;5H%F|3H zW+J?~2q43T>Uq-Cx9pUZCY;VT;bXcGvS?8ue2}OCeD44KP_?Qtf`;q*{)g3ZLJhtY zmcf?-z7!x-?S$GWmHap;O0US6Xd9e&t}ughg>#9dQPPN3`*wKvyK~1~b*;>pr2uMf zkg)-3R$tQd(WGh@5TyuFv|>sjzYL-rg(z3SB8Ns}SmD%bg>zu5t%#*4=&~AJR?6F~ zuUK5F3uyaDsn5L+ArO#qIHX)9iyHj46#TZ}w*|f&k1xAsb17@U(RUi-MGp~CVhZ;R zZa5X<<**K1h={7h%Ttmj0-w22sPWf?Cznr5`<{V9@Z^HXIWUE`^W@qt&>C!UTxy^N zTQ1mgfk8DhXanZlJt}R()Fg83(5WZj#04iVAP2}<9DI$(9meBSVM}-f>jIW;j`yC~pi~4spR~Tpq`?=%C53XQ zu-Gvb$|Q`1f=Ab*U#3i-Y1svy)XWoDc7Z2H;>ksk=C5}PB#0w@~fXm_GQeobi zwDn3Xz@Nd^t3m_z!PblXxj?0pQ)%-CpWAHBg5rCo(U%=;bX-zT;yH}I$PQ#=bbzBO zadeo%*Bzvuv;MN;MZWG+|NiYm__hh?(w){yX?-Mg$#it_AX${OJf1yrQVBsSnntHO zjB!>|{F!B!?n0z&Bt<9HYjWeIQxu-St~Ta1y+;s-KbeadtuyPS82YT(XcR|txX;-)%wl`|9qyDAe}={c(dX{^WKajdbf z(V-loLkGcy=&%&DG@!Lea76{xIK};aA_gk}yFf(0EV!uVh1@-4gGm7Jg?JJ8ivJ?& z!&h>AID@mpgp9x!LdFMprIi#PqsELVQQg!KQy3+gapq7nzamRw zc^wW;e92iju*#6Lw~a=K)_c9I_d$1pYhfu0ss69xm317w56c)eDL>xy*R;kNacr^| zVorobGF=nQdZv1qjHyE{I$MCLc#BpcOqGiSfhk~Wf=6jSa$_#VIP_KtOyiK^;!q)7 z4u|^XK;BM;bfx4{sgMpFs+&VqNTXRTlo7w_n<^jTH^gtN14#i$?FggYT)tz{>8+rL z5Dp<6q!UP$O{xTwghT~(Db68^dyC@Y^T&wdfTF5UREt1LnC*)OZst;W`}R|MgMeq| zSk9Jt4FcXVO-qmASW4!!I!}9M;GYuG@g*FRofooqK_xj9u6ilR_5pC{4i>u)?|u7e z)T>l}nD4gymII_dy@d0Y0lhscrSxpB7BxrvqDRR zvuXv4<(>)(tug^0*j1g-+O!k17L7IG*=a9jJw%6y4mW}pnTGg$6D8Sg{JSYs7JMyM}@jGGVhjiW^OqOLW>Bt{u? zFdOx}J_fFtck#5A)ynxIt78f^e=;7vuUNt;(RpsAi!e%ryWOzA8H_qmOVyZT`wa7q zs)xYT8>V8*&x>KIT<0e+1x&>;wbC%(s1lge_UV|mhq9B(Xs+&Vq zN~4@%zN;28V9*VN6nS2ug*IT21%r;lpmlZ6HD~09oRLE?hMdth0E(E>0YEJTW4jFy zkD3TTDx{4dl|ZVeNwr{rc+?PpsIAtht=Y^GP;{86ZM^~FYqH1#J5?vFp@4v$8-%oh zolCyRu@?~-)H_}#HbN?QI-R8x0Kpbm(rKN=DcLrs zD%4Gg)xfFiiLZxK8?;)dfk<`UGR|9#;Cv?}ld?Jxl`EJ5M0JB`JLv2j!VSIFfY3XH zW}-G^t^uL;A+&Ms`kFTqFFeSggx8a)6)0`)eqH{1oqrP<)G}jYNgTl z9Ro=90J1z4K(aH;%4VzhwiAY{=&sy?IUwUDSN4DaqWTj7pm_j2p6^lAMJ)_?z@0_l0)azCY7(uB@D7Ca@;E5qy!+;$On?M{ze-XgW2H3eu zA9pcj!+Z(mOORwbErQmaFWJ_W4I?J+sH!d>e&8Sts5&94MjXnuWkZAadxLk}mJL%T zU{O^pDnDiNq%Rw|lRVvqL^Zer?mW+(YtG0IjY-LY7-mZ_TY|K8MG)F5ZM~Q^6Q%n3 zaTCvmaTA4c6O5YxN3mZ7aI{GJBu<=oK1d>gi4#nmfI+ceM3ueTX}#Iw+ElrTlUQkO zVFULVWP@*j%}8ux=mbM202x4*J9M%!#|94qmlG6X?gVou863k1@F9Z2<;?;QbV?sR ziRaF}>PJmD(?5Rr>D!WF2(@I#nKveyiud8E zochMkQ7ih)YE$tyhdP;Bj~scV*0d&irPjPi>#6nls6ed*skU*zI501-bIp$*G%?ZiyxdY3Rl=eGYe!3^w5=!lx z(&k(wQHiwnZGuY5SmA1=((d*NZ&acVR4SDesI)Pa3J_?imEC|CesaVZZe%P2R2(s- zgwG^m6csrEF-H_WI{=3HlLMw|4FkXcFpD+J^&cMsq7507k=hx|=s0Jm1`zK!)5Zco z2%?SypgL1K306&GB#kYg#zS>uWQo^ov_m!;+2}E{A}&qPOa+itfKmHar>+eNo8bc>c`80GBo29^4OC*BZaJ9I17X_7i;aQu~f~SGvrLg6r|RjsknZp z)hXqAcYE&0?I6$1R7^u^ax7vnMq}Y3+kI<^?ZT(RkXJ>2%W}f<*hqy6WMzi5K2xu9 zQ(H+L-3=)fec2D!KMM!}LRCU&SKwrLZ+zr}fhg4yrOZ~z z6rzv@K<`9}EMt2k%FceTAj%S=BxshYe&zo1{ri-pqOqy`(WGle8~pKHOA$%_K+&wO zXuzKa!m90>2LqIR0+hlhlv~n*tPLnxp#&{H3PVWlw}0;Ms27NmTxgc^OPch!RmSH+>2bcT~Z z*P&GpfO06+vzy8phidQnY5_=9TZUIQJh{ryY)16}QtdY_FcwoEK@!=fB zhDc8vYe5T0Rvv@{W)=L1s-Y3dfV&^7@Z zMX75WwSM6;Fw40FW~u(qu3Kf6?W9ynljplIv%;N1wx^`PtQMFxq=cq}xAkgkpj5?_ zLc9k`T~2)IcyEqMD+GX7D*2S$x3a?chAaMT=)0VZ zuT;k94mjg#T5N82j#M!aQ{;VbE8w73z0^9YmYVKU0<})0rRICQ_5IJQDN3bG)`C){ zD8+;YrOrdCX>GMsOWoyqo?z1V@ykp!E+tE?McT?IX)6;x#|=-A>Gk%+)^dt1%Vec>filN^tJt!g^RZa0 z^jNTCbhQ%L0=C{Cv=O#$XMtD2*m^Nyio?p#%aHA^dmL&hr@g3=S|ww|XXb5hq%5e#EdVvL66AIaJt{Tbldwb+53?Km@&sAYUQ1(DrmLYSkx# zqEXe|qpmoSDI%!HNz1usZ5-p|sXlQ`HHj1Wp45huP3Pw33{J8luj}ZpdEJ1dlMN>u zuG-gXy;QHFK_Yoq1Z~^7{lrQ(IRegji8VEctKs%m?{*R(2gtde?oAZUN-JlhnPr}m zqXAyZ;HBQ>M<`V7t!Rq#WJbLttvb@Fv}t)*qgsD$h#Ao4?%!*RpPpZ7{gK-gUn49w zDukeF7aIXVh2yD$i;ecq_#|(*Zg>uV*lL6%^Lil}nthfL>f!J|*1boEeq50vsdR^e z2_Z(d8BdRBp+;44j8*LpBM6-H2%LQcO?-h-ie*RZgsQcQUhe`U&$}p(f-9=y!WYGG7+T`LQ`?k0!$!ZZRLC$BR z@eHsq&vimzWo|M`ubrj%lL1kP>sHiMm=QZ>T$jS2j6mL2$yUBgr5U$bxKwY=Uf>l}+=V*(vLYu#x zT98iSKa8)ZbTYY=UP~+=LZ}BuA=JxXKD0@!r&1TDwcLGKE3@j|JOpMzS8buI=-w!s z)}M(zLuICPYuz&hNHqkhrDM1oL+d^GOWK!xhOI(1`q5S!{9%k`S4M5r4mGQ&a?|AV zD}_=d5#00(;ilS^La5QzhMQIJl1h`b{X#0ymks-cO0cEc{X$?%L*Z5>;njFW(R*um zl5rdK4|^RxBW(0Pfv~{n0hTK)_eES+l z5@(%JPN-wWWY>o$#bjJC%`0S5q4ctrp2-bXNZas9Voq@|DZ(VOx9DE3hyMl4#ACMsShUsX+t<4vc%_}MAXnw&Dio=0OfKrVAlK!~?a7rws^wjgbEryel$<1& z7pdeD%%ayAW-eDrZib)LhboT*oI@(&+A9B(oeOtD9s;)nE~?CXnn%B>S#M zFRJ$d+cJ_#kgRf&m6-zu$(knF7J0C7yd6*{Ap9s7Cx`8 zVU@;=b+$sZl^R6`+JH6!ZMWmfOXATT<4=~;r*SN^^sGLO5asDAH-(mrKdEKGPj=KJ z{+uD(bl2w1k+ZEmb55b zBGz01TXkZq+77R$qBrQKve??MdiX?PFpjPF%~)vXsyIr{=M>s1r32V@tPX z0b_My4C)IQD~7RM`s-IvV-^FCwPUQ-=6a~H17oa2bN!B3d*8{0&U(pe#9EUp!QUNc20ZH$YLKiM2|{STyxyJ$cnk zbL=@3@lFZmNnnWnshMl5>FLl7)M=hNJFXe4LLptjCHhc~Glp0Wu^h-%J-G@5 zZ>d|Or&&eR80pB(BXYY&ZX49tKq8RnfRWpB2u&`H9;+cagmDI89Ktvt=zs_+9megd zm9ErMFWmP-%lgL;KY#lA=`Wvu{`~d(hi^Z{;bp^zS$IbFD8&o?Yc~kI{~_0I5a8iF zhTn_a-}SR+n_=`fE;zX?J8q`p5T;px7+K?M1Y!Nms&W+~EZjRZ5C()@zpQ5G)?@bR38F1Rm{Cw+?=hp} zk{7U`f(iq|>Pxc|Ov_D8#YTePQ&s{gtNN>a%7!NcKfupns(jC+(T&Yvk~D(1EQzeL z##G28NTQQ5soum?)U}CrquV1hB~7lbYB@6{xz(n^I~1go|@D4n4#89*=)tR%tKtrjjqn99nn_km=)2&*`dbXuC-?Sp|ZAZ(Ec zTOL;~s}FJ{=xV=W^RN; z(sIG}U&b&`BIB6e-7*+z3Ti4PApCM~Qnh=tRTjdW3Lng75ym^Mg)*dQIkA`m!p;1F7h?4`DhR~`PXMvgK&;Xz zaJxo@tBTsbSGxv zJQg=bA{BogM%Ameh8e*Lr{fYWqF~q&7=~sBFl>E>Ep|m7VW+Syvd?MAlGa$+Zcl0W zKsl*>jh>>=VPIIL46D%UJ!cLYOk>bxVA|Q3_Fa!&RPTnkWlYQ2R|uxDQcMd$Fqj6W zUB9e0UpBlqb7M&cOdz6;JCBfv&dR#ZDhFnHw-x;nNby* zSuii8#SrCrAXlU4N*JQNf-$9;|06em&lo<{jTkYsSO7lne5e>c(E#u6N$=y0c|NEC zJ}u|ar3~+o^qPV40ensdpXBMrmGzL$%76=D8@6T^on%^6=@zdg=V)Cx50cu+I90F_ z!{x+$&5t`)n{X;w<->ZhbIPvOs(cIgjfpH*)V3qS@L21#5EY^c5h2iYp~tiiOV>89 z|9C5nc29=;oY@?u7&9%m8&sQ*Li-cwbKyf>`KScBMC=L6aNi2STh?q%2;O9~mVo_i zO*mJk#*M6C6c}~ENHxLRc4mO~g}Xj`p6v%O8-|k>;l$K9_=j!+!0Ez|x&%~&P<_fM zFe!?x$DI3P21-8AEAT3Ni($*WQh^Ju#!NVP1zuf_`6}tAo!K;*4!^>PXO-p703%Nx z+_Eq-UUEea1p-EZQH?MfDWe%S{d)gsN$Vz{>WV7SaV@olZUU-GqiVKGYJAw!x~A4L zKUic+^@Od#0}S5{s3gYNwfT}k+^@Iu3CfO;ea}`#*027_$R+|d5>s(tS z$o6;u3;^3TJGD3))kE&n+W4|)0=>akwZ%c{484^C_yWF4;_ImeHY@N;WhK`UKNjMb z)6&2K2K>5ySqs0O^AujAI+&fIZ+DD2JXSCzP#w$x7=>V?-rGnwo<&` ztCywh5!Dy={3l}+GRtFmcf zV?&brOLAX>%v;sKP1CCyyk}lj$S2OYGAIk4hfjymSJAxGAOEq7zLHtaRjs!*7^AeT zur3(rWuULXulFyj=hwHdyYNfp@Jn;gY|HVX=U!BqR@atm=r8c=`eoJpine9_`hxV9?{UOhtHi|C`kbjv;6Ii-zm*S|PKTP85J zItKM!UyivtyF<~O02LJ_9K9TCgIrd6i&g6s^PSUm4EH7HRT;=!qPoZ*cySz52}l?*|ny-H)rLpy=fm{qzN&avjDK> z0Jhk1bQBGiv>bIg-KEkRE8Fef3ZEt?O_km8P+$O9r2yO9Lzyz>dLY~tXodl3cRm3Xzm?sCz0iz~iRJYoC zT3zkwoGNnSNo^!=I-V4gnJV|fdLeC{1Cr?LKa1R2tggmDj4UzB+E5J=6(Ui+-4!4a zNOb*ja%6}m)4L|hvb|}YL{f%#NZQLl6Cvt58Hv`hMSW5!sT^BWC4@EYrpQGnnHE*r zHfzZ_YJlhAQ#)Cy(!TUAr!RG=0k+$hT5%{?Zw(NkV5hFqat0LaMfNTZA*lQG=byiP4Tt}i;kPyk zC)X({rE58ZZnZS@fbqs~a)w1V#un{0kY00cX*lW52K=~4f=PB-NaHB5sUB>cIq#{f zfYTj3s#jC-al&y4;J(>2;A=YI8)x|6&1WRA^>(H&`^2o#MP_4wEnsWceAJqVia_W= zM^Q1IU|`VO0j-=t#!Ie9FPj}0w`w9TRlBZOT}KbJzAZF$^|oh3?$fix_M|cW(3-QE_)}y@O2&qn9Hz zK2C{KQ%yQIB5j^owN4aN+{y-K6=c?7_>^W_!99TVc3LY3Qqd77!VVEG&kRL z&!?RECiya0?&Q`jF&DrJiK&uxO9X%wX1%KQhThxzS!bv2PXVzCYm~@h0b(0KZ0F&o zC;BQT4D53~#?H|xTf(swN(>M?D8!bx!X`rP0btW(=eh1b=YU#`;boh-dNfrW0&4q> zHLal3EN0N(F4hmVf-_AhK(-3C>SvnP2uE`}!!Xdq5RL+J%|Nd7K+`7is9H3p*>tN$ zrBo|A%>?PzD%I+rW_nt?T{}F~i+bB#GQ~LG=|DJhtnl>u&-08komboys3Y)hs4|Pn zQf|KULJFs8#D!0TRzgL)(X3EXQ%SYq4GS&3q43xq;-c?{k2<#E-Do4$O)8k&jq(w${>>Mv{g)yrx&UR!B~|DKDJzT@t#ZnBLBwcTVV3H!`U;uwn)Ov;uvgCt zqZGY$4I0hRb|{Dzllu zzBUtZ6y+){%2k`}2B9_~)NHbw8n}DJ6ESdCRWm^{g{S>R0j(;b1zB}2XjLSuQsI@d z2WLWOMf(Qa>|pOH`UYf$R644ZLb9o5DOI>{AhAav$B^Y39J&|5-&)Oo{`B?JUq1i* z`Rn(dZsBL2vS)F|8qN!<*sS<5mrwls{brl@De7Swtxrbcab6_V?zDdZp*NYeISs%jE5@k-vH()P) zZ}kF(%rlPw=L6yMe&fdpp3jOS2{!1!-1i<}77lSQ%xuNQ(7N zrI2Fm08*^XAQ7b42{D(;v6(5MZLs#P)Uu;VD78{bt;AMDD7D5Awns6b0%_*6BT(VR z!Daz+xF$Ohw3&7la_y+a0SB&az_lY42S~0ZuMFFyI-s(PqCNL4xVJm^%B)JfWt{iO0wY31YK%aF%w=vfi)euy( zT$7z83UCd#bz zSo^^uP?5(uAC8-`N;b>6Ak8>azD8;0g|wt|5;)f+=bn&eyGgMHa|2g)j-NzN6BvaQ z+1u%T6Jp$0W-Dy7MT!i7x-#OxfXa>)^@1$ERPCm>WM`b_tirYnrX}aiRVUGIwlG$# zx$UBV{Fkpwh`F8x8e+q@i+8bM3HRDlN-uB?(u;}}uYi(fNo;7E1ym4TmTOsI`vlk4 z2=a2>fI?ky6X8`lNxxrGzW;8Yp44G>k!NGen5(u+Ogh8KpQ~=np{@V)n5$rHQ)G`X zweF}VZLF~x&}2LbY88$r>z*M`RBAaZhl5f> zW#H{%?OxkZYA0h}dM8?|B8{9{${qmKg zNg;|M;eU3D9W~+qgvy9ovmEcoZm>|u#x2LFk>SLL|E(>QHKbtX^#9zk*3JYm%(!O?ZE{g zSMNF!4#?Fd%8n<((sq;R>=3f`yx6dtq{1R)n|U!5DYI#{DV`o^M9HnOQk+m<*)gJ8 zkf_Hdk|IDl=q3^vVy_k>R@*i4)asEn%8hxME(C#Z4~DE-CkTYBI&}~@4)^Akkieb) za+I-z1bO+`FQN|3Ra0Jmn>_pz3rAv#?BZxG*^BADEiS+dAO9)NvGDhl$Fh9A{_{NN zeCMr~1wMHO-%)Iok&y7*XZt7i`{M-dnZ+Dk0eM{id2Ob-+)v#YOJN2ikd-L z!lm!ymyvM>B}=YF8q23@EG10ST~NItJg;%g1koYLRcUmXy!T;~@KB{DwgS4^?IEui zUBy<`Y>O_W+}jgf%SpB@vt<)oJUO?Xz)Umf3gj|0B1*S-7Ftt-Y9Y`?dkH^xqI&+}^sTI8tUbOp*hN&Hzr+wSPFE?5b%%oMs}a@j%MhKRn6rByn^OekYLw zeO|v4V5o3tRnYHb@BB}W4Q|UV-hKQ6zMWTN*)Jqj|iVl`1a zzgDs2tF=jL<24Ke3i0Hqg>qc6l4$TUbSMXjvP@6hbEFn z(Ik;e-G+n-Tgas>4xu`>(VW`it#7N(sU?lnCXS?0AdI(5o78q`U<>jI@(J>3UOvqi z*-{KkU598|-o+s~NU+Sy}X8aNMfl~hDsqL3%@Es8~Hi=go2x2;JS5EEZ zJMsXi+|+rh>4ItC%W)dCQ;4X079EDB$!r0&uyENpwWd!uFo-9y?^M| zE{!6R2sB!aM!S+H1vOW+2nMv&kCrlCByVV;;wcE7)1W0m#XQ7a_g^Qulh}L^HCssY z;lL}VDg@DCH4Z@3e8iQocw`KxWYZF(K&N*)h2x~{{xiIw6X>)noyHtm5NM^Qo%M_B z%U&)AGsbz&n{`NXon}Ux{cB(Im?={G0%negneD6s)~V1xmzM^v*a2__RRmmJj(+Kw zZQ=fqsUR*IwDrqwGw`HC)Yfv)#-tN97V(d&rcgG+#@oxIHrsfc$**Q>bM^A8%xK99 zwYh12p%Zc$zxLi8S@GIipHP!h4q4tn51XUW-;)=DK0XZvR~c^DHZ3QMq|PInqh=Ynp4I!8~~=cIphR z#-0Lmi*_lG0iAEn-7t#=W4IazO%Q6Gi$xDqc+5YKXfRGclXx0$6p8VU98OEg9s5@s zxV#o5_$r;RgAxL#9)Yvb;Ox5k=lRWTkz8|D%^s{ep#TD_>S0wK!B`56CZe-wH0H93 z?x%2ks}V*=6;4Pp+M=lI3Ox{&ewH79Db^~`Vyx73PgsSf) zVI|GxP33kN!RD<5WJ=n+0W$SK<}{($tyA241ukm~6BJP)ShZP!LkrSD713DxBssh( zHaNUF4bPEx;#BK7f->8=h;yJaTawAhCRH|*x0D3zI-gp?&dPe!(qTfo`SFnyGdEc0 zvLnE$pqU%;PJq+yd8b>JjYC5IYTcGqmVay#b$yrC-q;eIa>{cuX-l1*TVQ>F}SA zVT&u*k9tS2Wf^ns9N3CqmGAHM2$Oc}Kr6)yJUVP=bSkTK;F=-0(gv%1C;R?)S0Snj zC9Z&~M>EYa7D_)6!4f$)D*ChP)V;UNjU2zd5nQ3 zsH7^m=`6>r)|Q7~{6tk=qbga<3RU zVpdq&2;nuf4IorBSv@Yn?Vlm)*MKKxh^#Qa*=_(HJ~vDnS0k!WS34J~W;?{O1N>EG zWrcd$waP9ZK1WvY{cV`enn6OScf{oO^SSKAeFN9lC3*D;M-i^>A{-C< zQs?Mff}V=}B5LAP9WyO!amPX+-3lafG6diOcmSTuTiIwcflkhbx2}L{Z5{xK0HW&= zFB*kqIyc>|;|61DW=x|EO2`H+5Ie=8vc@WIn>+@BcBDeG2JJ{d&gH~c(~h)r9>^un z&7v2H72*^MW#{75Y%dbrDnD|a71A}|h_wGQ%d@79z^!BEuk+H?xot=4g}XN!lAk|) z{q&d5KY#xEJx(Rtc`g-imP)SE-YMEpZDT4iql_T2Q;Y-KOeO6NE0h~8 zuyPu#>^1722cd>*YeF*ZD})fV4Ip%dLS<3bc0*_(>QW%nt2}a^aRD-cOpTLi%%BCq z)fHQ#?{uY#GzhxhhQz$m1$NlZQiW@41X+UvRX~;Jdx?4h@`-ra%973&<*(WUGwBTN zT7v8lKvj5Q$ogc8tZ4hlg#fAQF4QX znrdR+Dpg~usicYmst$#!Eh}vk1|j|I$N(}*3#Av`B)6(z&k&`YC}NN{2N>g+CI@x_ z7yzb8E%fCpK`QAWg_1E~%KYUY8i! zpCc6utSZ&W1Wg4}T^_X`ss8v6D!r5*{aNM1XR}dKxy}f$Y>7QbNH2j@)fMRyl1^86 zO)iIf??lQQ8EH6rt2L1=6z8T?o(a)gbEMfjk)4X#2q^==?CC`N?_a+El~6MKxfY=<`;Aq~FjvIY5n&ftRZtAA?pbuTXw)y9^^Vuc z)iURmRt@(!a>Iowu^Dy_F+~io5pNw4F}vrWGZ(==r-p#4+EIl`5uoaF>`PCI%%N$8 z(UB{fd~*A>(?+yxbmXnNGF4JWUwl`|z1K!8fkP}#YgJ4wsj zy-G%3oz7$c5|B--Y*>S$!f?e8;(Mw2nr!q zU&^ns943UlkNsA7Ic zsTflORAzL{kH?oKEo<_S(`=}}?}TL9SBM;xQ$Wt+pVwASjgN`k|5&K88+BYnr)L~B z%^}x0&$xhLdoSq&!*+~APegcbHjhX!?lqHWdf>wZ>&SHK>DX6|^65RX+AHlU)G}OK zL+7_a%QVT5oekQ(-1wdNv^6!7Ee2S%S6tE=UTV=EO`hf{RTGxR1==miI@zP$Hm2&G zvnv}u7>yxd>Y$iPn~7ia z4G@(9k<-$^K@AWcR1oFXZ8WDe3b*(E67yCmOwu5jN^|PPNg6RUn>bZ8Vj+l)kBT$xBn98?Z4 z)gGq4lbFiLp%uLutyxSlW4*5MbR%a0RInrgQzyaHKmUj78KoC}R{56WvW@V{)|d{0 zo)Iwh@yPEM$VN4;<}b=7&-O*$(nrHZT&=BYp?H7m3B9JXV5jiX#}muqaoE3dmDmFc z#T5&+hiDZaF8sF)4{?R~VA^uJ=mS2Q#7C9IRbTe}_t&5Q=aDMAt$Yj#X5x*r{*Fj` z`gP-XM7ZI?mYBo|iJXaK-5Ns#J; z0UPwBW|5V^rty@IjBvZDqnzkj;TqmFkw#iuV_Byq=UAu(b55g<$XyeW?laKRw0t{F zdgbW+#=@ZTLec<5|LIhqs%v1Es*olx*WNIcXwq&SL%!qEGFhWuDv2TJrJ6A`v9N!y zIdY4#bqV9rv(_Zjw&8wtwL%AjHW20m6!i$5xytOtJrT#?M0$5332kkSk=fu9xb(r3 z9lF!09im*pG`4KZH{M6~yJTJDl?~59qx=b#S3k$EFl>Yg;WegX;7{>IlkZ<9=(Ee3 ziFy-#@<`&nYST$+xM`MIuG7lt`ZQDj{5$4Kof(O)wt4z z5gIV`0h1MSj&`1)>BLSE026yuu`>(6oCz>vl+eJrjlvS!I_;0bss#*2VF1hrD2}kG z5sz3KswZ=)o?LQDtm_Y;HM_78#G44uc#UH)u;*OtInW;d_|kNgb*r!8n{@jSxOKjw z3|*gkG*cZzuUrK+M%(3e{rJUd0!%i&m;;y!fr$wVFab=50n@7+u=gArmx1=`hD_fQ z5n8sphZd-1g>*G$g$T#!lW-^lL9OC;$BijArk1-q{ z`~?KF;;*TR*J8|)n=#9FOzKUp+J=YJTBT#qn)+Zp`Ksx^LGOj4^W$D)7Vkpg=@s>= z=wH>AIVovou}*WY$yYDTVwOok@ea&l6U+^Wrkf*rXac?x`483Q1)`6xSq-?vK(jll}iE=3la-ls)kDk@-gILab+{Zz%lVA z#c)gR=g-f7|ClnHORG2?ORyWqZa~Xn6RHmt2uT=Cw}zG{=hhv3Niv zXO8Tqi%Z@xSDJiP_W#`U7ZD7#bEht%FnlQItAi~1k=26i_K;VfOHjupIZ&!rN)>Tm ztwAXVO07z%z5N2}9g@XLX|(@*P)<*+?%66oOU)g>GN$3fkwoG&8S#lyRzT z2YgfQ<2;X^I6;L5>g-3I1GSpUt;}cvspLCmgpZxug;YYpR$eMSx0Q;kq_jxy>rNhI zQ7?-kcX2@&5T-m~>L|wc+Q{P`x~E><6aR~ArxJ>_)u~im=*+Z(emuS`$?lL%tFs`3 zO39!S>%Ra_mB2ZM4BBA-_gC)HCTCJ=$3?rC@+N?|MogiJeKJfPsEJKdvVX5H-~WF6 z%UADWt3-EXkFAD%N3~*$vz`IK)_JgXdUf@{yjvA$k~pQ9^0r}l<%G0!Y)N5xRU}RY zoH`q)>dsU7m1{J|!((Ty5WK?XI7W7$)acJ^^G{Bgw#k7VW#V=+eV3%+j=2n?(beeU zMk@*w8Xou~oIe=wHTrvR)tO5oj%%u#BczI4wM>GPizMimt{xKPnVms>YfNou8A@ zg?ik7PTB*;qkNJuV?@NdOJuz?J&8|i9ZN5qZMJK}J87J)vUjW{=a_Jvh#+E zfBi=3oU~RC%BV96oya++cT7>QLg+MJMvV(5x~v-TBGa-!E@c#XHmMD~aAQT4enf4J z@P+W=C`6!+sunLxTFJfc_?`H)HRToAjF7Ze8C9{#M>@mx5eqdS>JW&kiAm9QG?BK~ z(Re$$)*we^!>gsLw3lQQnn_?(a)Oag8@UT6xg#%kHvu8*6vIpm9qoD{R7XcU&kap2 z)zYhDA~9OO?^w4gg?c-*_jSh07B&7%cdUV-^J<}W?LjKd9(0jHt?sW@EQOqw2E%jMydTpj@gwg&Mkb2R>NJy56vy4QH(1ktIFuX0D zCdM>2e!#3+Ur^Dd(^q^!P&kKBIIAz65&*ijbP_KHHQmzT9ar2}nF(9(E*HwF!myc8 zAoqUbvddg9F>Q=9-fSUMbITZF27DPXQBS*OPT~v{8h_ffak@<05EITw*C>^IXPDQH z*9WEWZ-FwUtkLit0!r7n_v<15^M43N>9opBD5i9~b1rVfgH>Z;h88jyH9YStY=^Bg zTYpg(xx_<>ee1bZUDWT4NoG0uaG$tZ!_L^TptDBnIdx+`w~2IXKWAk9wAvo3cTO%8 z=LX&96Y-{S*3T2f;-jno_32yqZaxVk-+1o|Ostle|$=-9c3xOc4@2C4G#+MBOjeL+k(YyGsVR!R{^ zw`}~h2qB0eKm|ovT~Ikpv*>|5V+D12T|a)kcW>T2+-XZA5I;pY75|HC=M==x;l)ov zXXt57BqZX+$~ z8~MZUwfDY-OsIT%7e7Xpdaf z$AVlRZyr-}EeF{ma@n-SJH?c@4QnnZq^)vQSU5(1UA2=7#bOKO`u=5QlC1}>j6Qp- zQQZ|E%f>wLWZbHG9{9RfoSX(y-f^Fi8P{<0U2SlP;K;A z-?UI#fVZ%m`*uACUOO#oJdq@X*I*8qQ+DHbT+Z#k&>|A=4D{EPTE*1IDYqIIesNt1 zoEjeb6>7FIffA%o<~=2&&;;_G3e}dXF`yewAONK>P_k<&o!G$f!r5jUIJ}d_*(w{$ zT5?`$AcoNY0!*oEAeQT%ypO)!b|7X@T~&Q3yW$f`0N2UI(8GJ(?n0(rt8F@z`x4M* zMU}2bZH+kOVMz3Ab|=b`zH+Z$eov5Eg>#Z@MqFE~n5x(bB%R^KlDI*s<|$Pfmc~w@ z+w?2(VnEx$0JWw{DjQxc)~lXA7ywM29a9U^0&abwv<(lzBzLV|Dhcw)DW)pq4rXNF z&W#txH%e2Bx(}Ocx9(B^vOrD0;-IbXSGPL5@14@#TiSJVVmHuqzuI{SqPoDesUVtD zKAD~&mF5|`=%H3OUMto^PD^vhg;!vwE$r;qE~533NlQa&ykF${si69OLPl0etfOkU5t`@sxP^PAcGDegI3#9UC1Aic>J;H z?ho&{;@*td+&DMeHqNBw-UOQeNhXVeUfo&QbI#GxDpZ`N(%A~D#2*s3ccM!&Hc(y9s z?l^9P8JOA~Q|n{C|D=7QWw4!q_+7H+vGRDCW5LjiSbO zR)>eDLewzhm@bAI*mMqsnns#E_d~cbv)qMQGHv+Y^v1mFtl_@OTn}XoR>HunGcoJ? z1hf2=S&1cG|J1g+#@gpHGS69c(Bhp{j=54XADHeR09tgyVZrUz^)iS)P-WR0cYVv7 z|Muyh|Nimy+vguYKK=gnkG34vA_G{iQ%*A(b2tWWcy2Wr*Vts=TXc#WEhws7cqNpS zZV|7-tKJza!K*Z0iF*u)>5SEPk#+w*F;YDfjw01Vn<-G|;lq}6$44w9yIWLtnzkTS z4yn%L1XzJok#g{zR0^az5~-dD6d~0U(FjtF^Fzj@q7Ex+l}@VT-TTNTRd{@2-S(22 z>Zqie)>I(Xp-A-tm=;MW0Wb|V)TrdBZy7g$5cnjrykyiget|l z_?;r|p_gjcOYt#cadIs(Xo6e~>7`MXHY1;YJifQ2FH@TgHFdZHYI#iJ(l8&(4=Aao zibRs2xl}uo)@m-z5^07tnQ0}4p@6HtfJ58pT*#@afcvRO7`c0USv?mcDuvJ|{M}kl zGSf5AEV*1~q|v-aa@E^ut!7=gig&R+Jn_6v1Fqp&(HCvnYa|YVacX3a~s5yx7sUG9YH*8 zAf7<7O2pIV=E(_H_ZW3im$%Dsc}uAsKU6SfrsQ%$c=VPaxk`hos;>C7t%(HT(~Y%F zd`&jPL!g-QwqbN}ro(qwVYMh6qhqRoE}(1oYVrG*<<;Yc)uPR4wF}F+Z@AU&jBwFl zyOJU-XhVHO5r%59p=xmqvE^GYi&pPcPR>eSEjF88@Lnxen6tuJ|JWZ8RFGUPO0JhB z)>ARLj^>gX`+UdC<;>iQk=*hM?G0R8BX++W1fj)M}eDwAXV;)8y}InmNu-WYl3MxE^vKjVeLTOXh{l@XQy2fPdu% zutK0F^ili=0a$%}2Z@22BUnY~^yI!8!-{fSncSvR!-GC#YC>57R+-9bOCgopXlj{F zmBvIJmQ9%LZOW$IITRtyQxl2`V)5AWt3Qb4sO=?yW*KN^O$2GCfw9ymmZX_bwdME- zZozdKDN;~L2-D264IsGg6e?*hPxJBZv^>p>edAMvwbVGWt2a**nyP`u(iln!eeBE# zO`Bu?M&*VU5!%-$tK2q;(4?@HU%C05{NEF#XtJ4gVYHoAtK0yuN+r^qQ7L7hHXEdA zP@5r06Aq>L58{O2d4)5P?97~yaMWgnC{t~ACTg?e)@C(jXRcGL+1x2x!%J_7&xB;E z0%iyHNiOhkCnsq7+)R{)W9VwlM5(y1gwfk^h1ZS=np^pqtVM_>LNuYYqCbds=~YvR z=1OH_MyDM)n#j?F6gz9#IOf>?>vk$<3kFe|h|*k-RS;gs6keM|X|AKZWX3H9q-i2e z6Etg2c`ZuQERb`Maq+74+2SQolN2}zj9TOaMaSz9s9MvGHIu0+-grMGw7-4&{w0nY z(`n75Ap21wii~O0@aSvnpl0~vdDMhoRL3R#MDzY-=cVKOi=rttwk2tN=YMi?yb}EpTfuZjr62u?U(kzKGTo zk0F5oRBY`p`A*(FB`3R2BM6erj<%%kIHs-~^QYz*^cOf*3CD7t<%$Fsb#h@&ZOxr` zxucZ6Qp%m0QcznYr6_9wC{^M*T9iae|`Pq zQ}~ED(0s?Vww{2G_{)e zJQ^kzdmE-Z`jf?DkExjAluv*DTs>aT$^c^3L#)u8aASy39}9?mycvhsp3s`z>sc3K z)4M*2Ow&0`v&w{D(^i?LIxV$Pk&p;54Z>_Urp2D+r4z~dJw*bNlRm5m3Q0m)jrCTl zJ8Srufl>W&h;&8?r#$J4Z-P;#6V6H%@n@lU1fwd^T_uahTpjj$kj?tKOhT94beYlt z>XEAw&}Cp=g}SUU4c5oJLxWYX!N8+)@u;faS60ybwi0ZS=IZZwL37ovxk_E~dYN(5 zXGVV?nu`GL?0qlyp6(1uR=*@GlkW^kc6v!Rr@6-Y5H)BppH*k_Ai`RPn$(`j8a)>1 z^#f`nS#^+J{|X^Et2g^q)_}8z9(kw((AyMxrINp3EUOZFH7mKbA-9NXeT;NE)toV+ zWhp1Pm-L=eVJTNANE%cF)zX5a^C-79)Usy;7 zO+$B5E6zweMCrP+%uNsQs!9n~dYAmZ&a7aST;woJaODMgW335>nT|WBV-Y_^8Rmtw zWNLqLhEcDAVFfkU5{Ef0&t z@KEW~+0`VW8|nRTmz5KCie;k;cOws56{2C)T2+`Uxl)Z0%O%(oFef|oI?I#ax&*6r z8T_yk3`OL+5^V2f@a@!BN*a&JvTU6~ja9k&4LF?&PGvh>d&KW-*0bk1hKN(hEmxth z{;=6Qs4rLVc+KjoDDF~two7;Gm(-*k1W+GBZXs#sRB$RNx3-Gb*-T%#tcfCWbqXO2 z{rj^c?5O?wCxgMbnsyzZS#P0Jbzt4ey?SDb_ir z4JTGNL%peKnb)p^bglQB#wV}*=h zN`!y$99PuGZiuPqizPAj{=Hr?#UC;8Kp8QTM1sJ^f(*hay7Fd^)0Di3vu^}mgJPQt z1iQ!e^LPAgm>2-(uYmIa8~_Kv9Sv|)Avq7c+5tzxEXNM^K{v;)bko00BF%SRDr=90 zwBe8EkrtjaZ=7FBG!~w4NtS<`5-t2ZLJ>#jZoEdc@ZVcV;dHFB)LID@>Ep}J3MDng zEY<%<5n6g9PC>Njck^6XP7^J^6-G$5M=U-Q?YXPI0@0RAv@D46E5s}y28caDi~>Us>*{&wQC|c%wM^ncE{-{MC4Y87sEdwN=blqz`O+ zf>;E0l+GIQ?wQn7_kYZ-fGU-RDl$;I4OGd#L$k=j3RD4Ab)afJH0@2IK|x7s4mr`I zx58MiTg8&=j4^&$LSZ>9ZP%g)mg;KJAGe`JwCL9yH#&8#1T88-RDw~CMMsU`SdH?k zd?iR3$mYb?#0>OS1{#!sCPHkKFQRahKF6+zO8N`yNyjdvv4h5mji#|Z8 z!lLh31Hh;+YYhvQd6NNRlK=MU%lCi%`^OZ&loni({E9E?+^B9@>zv27Z~&i3p)&PdDqC@sxv0^{`h)TS+16 z)cZYc6nh(v{gg>f63Z2q#Y8_gdOCRxR0$QsJC+bx|>*LKhwD$IwC)}oZ{Im(X$ZPn`gVJ(< zmeSJK=Qn5;T2NYrs=YC=MvRQ;A~8~TL|W*LCI&Q7Qdo!H(RwtYo?X!7zzyzuHB@XO z=Pny|GG!tsv{cSE3M}7g>Ab8^{)Df}(T5F718c^}1o_h#uu2=5QYLbZLrQsWeRV!2 zKnjq$d)Aws$PKE=U$#_uN||RBSE5<}H|w-Df(|e*NhH~6!H(os*Ff+7+4>E{~A_Yj7dY^HZI9A(e)Rr==H$LIR;okh(b;aa$3;r0-|mowkM)wN|aRd zDheSjKvd0$YA`j}i%TSnPT^1Mj&($`z*|)_eon2BbjlpCbwVMJ1%#@ZPz?#BKpbrG zUDH<#h_f1T(AZu^oY{fhDmN7NOJ8{*X(e_)JrK)+ScqPMMyi5XWi(RaV(}BS^2;_p zGB+fODrEm&f>wBSeePxv1sBu?jix0*D->g9E3@>}6`eBXZyha2U4i;JI9iIQu58o7 z9wu(`8o82^?b~>o0U}pmn*^<}~rd2S(EWl!G#bW^FyZoO!go!w0$ZEqL3 zD#dbDuoQP5OhI0y$*ZA8)Ynjzq#6;SDhO49od6JfJyd05sF~|z@MF^@{Jx)g9oQ>t zOm+KsrPhIUCnaZ=BA4i2WmbEQ-q#Up$W5n%TOMR!0M!Ll%sF0Nv46?!zs;14j|Os)03afVGm4FOUd`#IrG2lJ?h&he zh^dAlW`xw$DJ($jybvQ5*5tEIz-*$&G=k($PP`8#!wQyz%7a<=%nDRe)y)1oDluku2smXP79`KAe?b138e%UU)|c{fxGyThzY2( z_DDAltJtr+3jPp*g?IQD%6=z_24U>1CdsqV-9ma@?ruAcjff zc_D^2*@#)gj--3DPb-q)XZz{?*w)30^rF zDn{^XJzhooUf@*)f@|p-rkbs{{3ro!}!GTv8IlmJ@gEwRV-$9aHR=8bQ5Dk(cFxwlAhdrETz#eI`^O33+DAZPe`guEmV#>Oz1Vt_PWw(d zz!ktHgwWMx-82g>D6T@4-xyjWPDb=mPViqJEd!ziqA<>7g|4T=5+#x;1EL(Y={-uZ z+Pl|e!Kt28%m~HN^9HAm#woh^X`&PU?jw+ly{UIYc?zmR1*4p87gd(|@c;TyRm@0^ zBJCKcI*X4$Ci)0k2bD2Gn+m&002M$rJns$r2xMXps&x)o?zyjU90v}aj6?7dByQd% zHl>;emsA?y0=Oy%7kmVxeieV?@5$^*6!S)S-YUg-`2M(b6;iBBBs*-JM=S#y?pF=e z5^u7<{>9U%rF!Gtp$3~dL<7I1HBqHV+BSiUESgz=yf`J ztvxs-@o|W0;!q{ZOO?p;E~jeV=aU}^Sb51(Mk;T}hr2AWwBbyVe=!brwGFJZULAb- zCNx(d+dbKsXRf;a9gq!V`*@!^$yPxz$@6ZiN+rB+7i7{|L-)loD(>Gf`LPzZ#@%o6^2=6$+|x!>y`to(ZafYL!y0 zwhD9}VwNk3&@H!d1w%$)VuKO&oD$&?~Vb!5-80J}H z1v64rXpJojt9T8GWys)o zU6t3H*etKJ-rO_~|J#!*tI(Wa@GW~RF}0_8N-^hnbt_E=rWCqj>r!glwALrQ zq!pXXs51y9_r`mrq(zo>Un$|$q1M6k%wNt4t*bMCmAcJ=9i;Lo%Chc{36NGm7PRgs zckWXc3y&!CEw)3PLOMY9wS2j0<2Wor}F|W zng>=ulB%v9e_&u?Tk>#{D{;+xg&h4~mS^V2C7ASCL}QXE*>DkQq*JOoi)yJXeGu)8 zL_1m$X`#vPHJOvzS7-e?uqHzhc^ZOkaWZ5LjVALldPF~!Yn5=S(}HQ524|c(LZg9j z^=mW@jrO3?4CmFQ?-bT()H6R2Oi?CRXtWj@{bPDjz5Cf_{iDh3O1}rz-Nckb!|M)3 z+H>L2@Y<_fgn_GO6jeEM6nPzbZMpW%YA4k>W}U4LSfy5g0Ic%BYNPtg>u06x{7~lD zc+7;(YjQPTA{ym=b#W7D;{$Ua?^7@4#=#Z)^te>=iNUy#M33@rktRwLRdVyXIiLXUEYYeWUYmk6df85ssR_nnE zCp&7ET1!_RC#6=o70Aa0BGAkU!DMxPuLIKz(yL~g)fBS2`a<_S%{1q%T_c6+M0f&C zg&7rP46e{-pjpjru*X%$d3ssqjc;}aDLX5-ak2<(DwqloQ>bXukA3Hi1 zn|L0!*2w1GuK;TG6`k*&B~S}m>5N)wyy={}PnciA@ljVkQCw+Y>VhdDgsv{Ire;i; zMNRN`Oks_1gT_+|wPtI%HJjYmycpmm;DvE6EA%(jix;S%+VHY%Upql0ZPwOSfK(4D zW`yEsYp(|>1r4-KsWu*hwbJY!Tmk$|y}QIyz!YSba<*MCS?0t4>jP6UcQ`uVb%Ck4 zdMfD(=(E~EsZiq?p-qJ?q{C7QEu@X))A5DUuR<|U=c0|c=f1-ETBFo~Hi}UPu7J4G zx=}09LD@XDq|%@ks8yU=a0N_sp~M@1k4#TumN&xlR++`a_s6BHkXdE2(!s3in04YV z)POAcCi`n#iO5oYAK_5v9r9$B;#$@hc_)Ls4SzF`bsl8xQyfyk1armWYe59-U)U0f zzVYw~Ien-^s62FW9vD$wzMbrt=S-4a{vFU2PbI+259lh6uEa&X3%VNiQ3XIKUEQMI zfzbukRj9c|!lM4^;7|>!OEt(dZ!>czjW`mX^75XHRQ>Wsb$HT-Ge!QzxR}*8p3ZRd zZ+*GNZ9dja!&qaUx$0JI4ve*E2q&=C64q9ySe_$7Wqxaz_A6V*8>N=dbA;;^GIhTAm-di&MzY`whnt&7hz zq`5=&{4c>Y~RfXp~!7NvvH#-&(omWiOmw;8!D>i=yLO_(A znqYN_vZ3fSL(!Sy#;}Sjaj5e)8Ey<8xMex35NpMR87#0BVeOGf@R1F*CCJrz!-c4D z6mVE_O*>#Q3W$+w>wTkxy37Ko5|eM51{G(WU`DD64X;H>1+P9d)6E@=!T8oCKFlx$+@J2bQWA%lS|PH8sJg|f@;4o)?`KX%VcV6 zn!wWR>>y)aMJJE%AR{|a0NFBuMYYucvX3|8AlthVyl+XZ)$H1`Qgmkr8RwpsTu@}F z6eF*;Qz?$gHiBuS7RkT1Be|OVwFt-ooSs64~qmYuF5mK0$Zh8qwmAM;~2B{f;JjQ}hS3A{+f?wRCdX{2vi=xq$ zM=?mPjVN}|P3on@if7zte)je}tfHV+9n{KsqBpJxcV3g$XCD&w#!v(8v?hfxB&=CG z?P?5N)dV`BrR>=$23IJwRIL;fqjlv?OS~;?pqrI|X=~06V+$V8eP@I)v*(t8dwM{h zNRd+2r5i%EJUMP)2!%?j1UGf}$rl(xx6w$$q)%MkptEbtt(&Ug2F({tu(Y3#U^Pwp z(qi3bZu&(%v-n2Ykkr;2q}3re$JMHFn*rU_(@R(+s6=6RS2m8Vd_o4XM|n? z-ambxyh`o361vr^)UNZ~drM{=myAL#)zh+_mYzwfMIJb2y6{DDzX)TtGri50N{$D{ zKHhx7*kh|ZCHk1~uSd1bFB{&UzO=!>8cs3#w+zPdqx3;?5U#)UWp(0GI z2p^0}6tJEz=YXZwHatGljR%Gr>DAGgR) z3j7>eR@KsdA1G!nBg8&91{w+@L_q8y5G#63bq$Cm18X`r2GGL%5YVavT1T26QVWJJ zcuS+h!#-68jKc76ZAL|Qb-<`9gjKoW;R}Pp=};L!43k3$tusNa#N=?#>JhGaj1BjO z8tf3YRF7kzZId`NH$$}78DgTt6f;E35VI@{s1jQgSXH#&2ZXuH<%xa=@n7EqKED0@ z`}d!p|2XXp3eGzlzC9lcQf8g;k}I-|Y*bQND>%pl2i#%8~ut+IyA)MA@k(^`^^|4?P{V;EFfpFLR6Pv9R z0$%U%l1xhoDF~^$;k6W2HC>@tXKzsQba;tSBRSV9ye^NFl4RIkNqF&&E6pXDK6q$& zQMwH9ntGB?-?DO0S~sN?q!xL@>%>Zn8s@E2CPHZyYFUjjG-5}DHtBb8h7q10VniPh zGh;?L+!Qazj1Zn1CAh6l5|yKjlt#0N89{YI1q4x#^z0N*by7I%*3Y|Yg))sTG*8xV zOI-@_(Ov??4keW`Attyt@x2>BEEi&tx(*O~Nh+1w4J5HsdIWfKW@&3Cb_Kz#TxLbe zJ20yX;kDncV6yVsWHfkks3|FUJfs0oTUUrtpBYg5c(dMWa^Gp;>(%DCD}Q##X*;n? z2*^RXHCb(@)|Knzl8(Cfk_|be|NZsPKTmsHRd>v2?c?i%#qd+?xTuC9@DkcXWo4>LLOSL*QO*GCs<+QXF zDymW&(Jxya;LF-VQB6MO4g0bNa)~z?$i+=Mxioju9I6(~BbVe{sOq}-ff0r?`s+Hh zqZB5s^GLDx+Vac8AFktx=NX&PtrJZ2+N-*So&ysMHq$z_7m1XEtWS%aB_64SKU9)m z9ch7${~D zts2)vRUZ6-XzEQ+J+Wz`9j~a&BbsE^^6DaP$`zF$8i=+%(Y~yYIigo~ZnBA>h-Caa z2FM2U*V)OoO|iL_Uem)C_?(aIc&p!8J~GaP;K-MF|IeN1BcpgUyZa3v+37lI8~Vtu z+uup>I+PB}Gr>B+7-B2dgcnS(%itAzgcIhLdn7tXsI%pk_sQABxiucbYE3ppibSxf z3Sm_?;W@&%>Fw?`q1A79pB-jQR%mcNG|Z-j7R>p}V77NvxbF`48dakM%$T;Oy0PS; zVFqPZWz|T4EFzLN$y$}>axPH|oJ+*H#3Hs;`0NwObF3Xl>VEZH+iGH>d8_m?4E9Zn zB-}~_scI+HwypFh#H86A7LzAqo4R@O;4&h}Qv-Qs`{v_kJFSmitZzQu44(w+6Ao|W zvLF}EB7$7eAH;by)3_m?>ZZys=lG~ypC+=T#1TXsK?JIzKZu%8b?!z~Ml4|aq6!xf zpGY+w3ur;Ax=6KTSVysC7ADyvI(u1!c^c8-93svk-jIX!zM48ve1ZUJ>wX0nmtR}s zn`lFgM|UT`PJvs9AXp6qn-)!z4?BUg0=E!1EAjHOVwwk5e+Uth3&1K3tbHWcu7?nx zS+~z5@Z?0p6+{s2JVdMM3gTu(W?o`x`-Ib0qBEaEIBLR}N;s_1R8nLnm6Q9l@7*;? zIU#Q0c=5@3Q82iNc*i0v_0^`grk?vnw$c3);g@e8#}w6oF+X4o#}IK0aiQx0{;d7A z)#PK}sz2)kTZ&yTA!cCf5{xX`#TG6h!d6-ZwptZoWW4(b&D8GVM6`Rx0?!3srd@pD z4C2c8iew=JUxif{$&({2N{gHw9^61IcLS01a)4G%(h6cLO>7N?Bd7aaIDm))h%ijl ziL+`96ZN?62b_uP^}{@z9j_IJ>xc1IfivK2Z=5Z?bogR_ECFkWYLVgeVf@t{uofvg zVfs2O*7l*;BBdsWb#L4~#NET;KX;;Ej8b6mi=lhAmtX9<=6&+>6*zhba#cdEoM(Ff zXy*8}i?#N*PfjA7I0T6*A<;e>YS$BoF?KCHXE?pLeRh)Jz9C3fE6D`8Yq(Llx7|AS zCgq?8x^T^KUv%L#Yi>E1U|L-zAT=(UR$shU>W%jnF4ZSOTqc8me*JOzF~w12JLZ`e zwow#u)dkkb2?v)G##mBiHOV5;d1E!FUcDGnrE`Wy0wH9_9TTKXx{yd2ZAr+zMxyA8 zo02H{^;T=2-*jp5Kk56cb4{!9+f>s^Dd6f3SDbSl_s;`Yz*Y6QS`SYJXyieoI~u7_ zmq0wNN26#N2O7QBOHb>aj~P(1mrfFFVF2pwoLENfXF6k@;tk_V8^5&oi1UzTr0iqAt3%f1{0%vj1hSe$7GzhB?8-_^=w+8`_Jmh>?iiCS zo~C|E*%d$G1%{oFVOhwc`LqehMBe(JyNH!WK_)sWm0;Da|H+Pd&LmmaLW}`qXBT54 z&rRAo!vuGxx}_R0>~suU+YoWwp4TvUG?$C1B5SKgaIdXvl~;iaOzZmG?|oy=Gfg(; z4$UjVl1|`c zj3ZCLDx`^mRW-9}TmO9ORj#$z1M~bndypLp_SC?h*-G$Wces98o)a3sS|c-Zk57={ zfZ$NDs|I$V#-ntWh0bC~4F#*>KZu%GwQY@;xP|z{tb91}QLjG@R#GD0fgvLlu!{d6 zYJ%0dg%vIbJ_suWgl4kJ!pcw=tCj?WQgm7ESZNSM8Gfk~befPB3cpm%kZEc{r?QGM zWP>6b6vbHG0;k<#OlIX6x868HFeq46HLIG^NjgI(ouCnf7*MdMX7&`46mAlXbQw}~XGENfHq07C%ix&V`Cj#nwX)(b<} zxlj~&?k1#vmP`LcL;XCwg5XLPTv_QYZ3`|$f06*BAOwJ&6<}GsS{~1mM=_C6Hl83e z54oR^VW8N@Cpm%)6B*G7Cjb~iKLM~B0QR!k;h>GJVXWw{5uaIOMMQie;&Vkhp)22> zyc%y4?<==H=%(2lSG_B~U;F$1?Mk?x;kSN@Zi+Kzn>EFUr&&oyo6q^>QweYQdCXot zp#s4aO*hps!<3@AxA z{?1mG*5VoT;@*gMQ?Fzv!V~3Vi#^5YRVvNlon8^2DYO=}*8Y7h2d#CdwXAe8!vnMy zMJwuKL95D}>|+j%;1R)1R`zH5?Ct~&r||sB*x?2=oFIZ4!Ah;OJDJl@YQ7@V#;Vxr zGAc5_2r$~V;4&zS1z6SA%eIhK1Ff9o%A%K@Lo4dl8?7j30BE&7t>_7SBh{Zwul`QZ7kA1T^u-~l zN+Sw3@y1;6mUO_(!&c$1XPl~TO$G=%9m0-tL1O>zm&jR-5sWL@Z1yMbq|r)M z=Y4p;AwdofK&&Q-1<{owy0RkldeNoZMVOP^*Vh6oEV`z>q|-A-EMwk_}ygR!2FmCq(VvPz_} zN=gKd;sgX(75_oh#Hw@4Dx8F8kM|#{+kVM1JF}5dWtuJ68hO&^?+0k zsc_}tgjXJL9}A?qeg~!7)mfQ$O^Yg=cvv8+sE?H#0(rgNkiNCjeg-FQH=tit1-SOnT*b5N zNH|kP=R}5Z)+$Tm{KDaeUHOG3R<7_`tKYs}EY!{|E|D`HY(B-V=Bo`zu{#g0Ah(j` zR=WMXeYu5`4cT(*m6;*e3}8LNWr6%_uvQrgL>2bQuAYG%lB{RHExMcNGUA| zrwh9Tlire&rb*+L4ulE+R8edK_XKt};xRiP`;D!m1D0$M4lnFXyttK-t@&{dxY zjBYTpp8E=`zQe)@Ro^lg&HB#MeCA^W6w9}Y0c>IB)h#YGr-9x)y^OLtewrQhT31Td zl(cXpdM%~Z(k+>LE9l(T%rB$-s$`fgqL*@@*B!mApz(BKdQptI(JSInhxV$u5FC?h z1j~q0$~niU46t(7Uou>wzl4(Rz*yO~8=epgx@G^#0#<|Tz-TTFG!Qh<(O|XX{fA;! z1M_D3u&FF@)rUyl!f@m}!w!O=pzDit<`;a#O2r?vX!#dBKLX*=;O|3T{rvr^LT zZXgT@t5|ZCi$>VLNEA6M#%+hWuuf_2Wp!h|QeiIWXhC!xLUd(W)SC_|4!_6TDP3Jt zzG~5hKG(SrmPM?jkm7YXCUQ8|cFrg3xE2-m>r3T)uF$VX@z@&2DwJX(r~lPF!W3h? z!fFx-I~l^(wp|^!-!)7e?HliLmE#iC(pe$f#g($&)|am*IwL*PI`x;9IqJ)s*mt{Hyh8MIL)I9>BT@*&8TXsy3+`@LRK-*QyF?@olxV4 zJIxtYEUWmJ<+;R9JiMU`F;@sG2D&OkR~glm7`pdFul$ORkL;s~G7HzV5KBDCs_Ffh zS6Bt>zy{5zC9%X5Y-WqL^o(MiGpK8xF~}&!pzh#|*|y=GAeT1lcjH z)$*{YJcx;$nz8x(PDBs`Vu0AkCpm%$6FD~a=J|#EVHA-y@attS=s}xW!$6T=(}r^f zibxwq+Hh7w9=TCx6IWvw`o3aofSb5R4L>;BKc6`g&pRQliJ+~wQpkuUwaG|4)p^U5 zqn`H*+61#kQ+P^18#7E;>hA^GrXKU+p3{_&h0Sh21Xl4xR<)j?7oH&Dyo&iEim|Fx zhTc1?99RWbu}WB_psPM~mDEDUN=E1^u<8J;T8~ZpqezKl+0=Uer}Iqc*e#iYC@Pcx zxdMu!qd}kuvT2EIVkq%GWRqviRmUoncwp7tBj2oS8rtOYjk8`v^?mXSmHzzt<2nWW z{$&bJMlvT4d0q$P<3RKX#2Og zcrLJ-*I~jtQ(Ywq9dF^ZTpTX>-2HhRqM@CzOu_(KgK^N1z`H==vgr}J$=mo-kbI{RZe1k((f z?DFDmMrmYy7K#IFvT1@@5UhEEr7kb7L$u4rPY%%(H{LbdD>LEybA~pGp3xKrO$MS> zuE|6;!NQ05Lu~`)b^8r-%2qd?e2+*~2P7E!WFXl7t_z1!^YY@Y)O0?2_8z4su-2&S zBEerlb?c{`6aP7NCvouQtIM$;l^S2u`XMsjIc1w~eH4cW9FiXw#@P_(2UHKmM_`%#Zgt)vqdPrj{s zd|Em^BBFVAX=^!T)Lt@*dKK*Ycr(tfrL0;SzI@-^uFKD$9N6{2uJAWuAeO^O-yei{DN1+J8RlndG%13iO%bIIJx7sUrC=HflK@VCd zXfRsqlw?||)2!Hbu8V1h+^l(hI{4zI&A>c+z1EuQ+d9EjF1yI!vs#HhT2^1CXw8BkVGpM%R!g(T0+#SCLcvQMV!d2E2 zUS-9l!+OATZiSS7KDzBv4kuaRa;O|s4Jo9d^X;w&ShYan)b&Zl0oHBCeUZKr5~p4r z_l+O-wDDg>L3+uoeIn$|E1G*Rr@#6YilRpGh3l)p7x1+!zEYeBl){@xV#v)lZeEh% z^eUKE1Jia60i7%#qd42SkHsw%*!5Ay-PIDbf?xFb3T$=3l~+V+1HpB95l2@cxK4<$ zaz|HxSsvUM$C=FSiQ(evnH*TaS>re>-xGUHjpY`U&8sn-TLr#=ua8f%=K|syYE0tR z>g7e;S_RI4v!kgok&Q1LS}hlGv%9Mvr=o$c;~6Y8D$L7^xU#yKY1G&eBg=ysEat2f zlxJerB$1jhKREII<>abP-v??wZXPFB?{I`@he~lYDK^?&T-51tr_F;3_f`R_nnBgj z;^Mna3*fp3m1jVQ^J-UBV#tYInkH+vZM?O51d~ zKW1~5T~Df}-iLU?(0V0Q6a`$(ou}d#zE-e1@r?FU>-54s)jjEjqOO$W zB5`trA)iRE4{=QOOiuHp`cR@igw!e~wT7yZUoWXwtctrH^Sht8G8&S%f+ztRx`{)P1~J@6qTb+HK+}^tWaHj$2j(1He9tPwSP8-GK(c z0I)hbWvj0Qre!Y2kVA2fLRLbvZ2b1bkW#+c{_>$thLE ztN^^AzK)>2lmfs$0H$^PxIF*{fI)qgR$qZv9=!VCmFGr4aIMFyh_3>?szPur4UEs< zM@&{+FDx$Jy!q+vujYYfEqB$mzYYwvX#qy@13>$Dv)&5wexSJ4sB+5+(*bY;;Cee1 z0Cx{KML`ORt*Ht#wU(rc#;GcCX6;cKkZ0&^IqlHfLakVuJjan%74+sU_;ukI>m2va zfM1XNqP(Wy*ID_M<-#8mYcq-sehKxadb3x$Un2a1H*C!3oibABSpIuKq?I|4h;bv3 zb_BnKO!rGP4=`@xQ91w^0M=+X>rj3Pncf3yo>x{_RblrXygC`L;FlP^PnG~nZ;CY! zH0L;g2B4h;Xs7T?4E-{FiL+25op#;`f2d@hyyZw{1mhLD!<2H2KCN9j?Jxppk#<$} z%Lba6%eEv>FGj86N;FF|)@jb9jwRg}qgkm?L})kAY!8}IQ9Lh?1bF&LpmnM#rY8P!r1j$xm)fq6s=xdghF9)FNYz=Kk z5o{vIUK^;^@XC|n#w(Hl=cU?9%i^F~YgDVXmzI-g-F7d{IVDxCy)>9M0I}gsK9#VEZlZ0SUbrv2NI^9#&JiTyR1oWzbUeH~UpbU+$SR57s ztg3?5K2mGfpo~>RF(#xJqGNGY1T;G#&1y=?*sQR`)m@wvS?qmPpxf~a%cK&LsugF8 z!}R+iI^7k~Jce*z1ark2FmxP|Rk}Hk!{0bA5{K7@XW1zI1$ceDStq<+T8-QWVS`n| zUoVTK9|mte7Z#UA(D4G#K0e72tP(Q)NN)4=!da0$OG#V-ucV`O9IJ#%-*0RlR=6pG zzE=&ff>mOv^it_A)#ho2gCd~Wd1%(YRpO&Z=GgU`NczAM*Fgwr{4JhN+ zQzGf@{??OSaY`f`WfZK??gz?}LU+>n_QbO3uQRTD+AJoPqLLwb|0>Z^kNg|L(Uhp53Yf0 zN0VwIJ;4XJLx40?pQlDzmHo4vpz5~!XH_2#IpPss5DWx+`opa}8sbg&b~F83bDQPE zAJ0=vchWLWo4NSK>t)0Hk}PR*vAj1J3myL9qABJ(QIGS)&gHy38pB+n18wM<`h$SkD2Uw~_PNm4F)wV6u z^wjhQW}3Xx?Ccz~oJaG~ zWnxl6IAUp}5KPaP9-8U$vb!z$9BqzwyEcP1rn8)R70U%HMw?QnYd>g15d*Yoq55m0 znLa>Fy@}pUZ{{F&G0`6?cOnB~u0O3NWpypAQXYCpCO~Xsv+0KQAD$EU~y#X+*oLz$|)7R2+)~9v6;KK}%sm@!@qX{|%8(C#?57R(KX*PhYU69pVTAGX5 zREyJh<6$<U+Kyaq8@obs21h4tFLrb59x%GvS)`FOr=m#l|oF6wwX7r?}}XK zSV}EyNW(%3YSlok8Z4yL!N%E=F56FPuR}ixKPmjAndq5zFQDqHx#~N9zkDE=yP8R7 z_o0uM$P6=S^wo77@eofS*mkK|afD4fNoSi^t&z(pQ#yK(W>+R%b=&*_(S0EdBc9)4#q4jC}j~_wPSH|1sGhQ2A(Pq|OO~HaHs@u>trmvLQ5HS0gp%A-NJ0klCOCU+t*2xz6R!Bjm-jo25qHIZ z{jn^Q`60a&5E~$7yjK&pct8vgD>7hwutQPw`o{=HkM9#3I3@yxGNsP3ZsTZEFzY6~ zq~;vLYdvOBpBc=mMR+A1yVyGC^d6Mc9@R7`tq>ox5(9XSYs!5#*J)kUxx#MDdnFe(Ltq zU;$XAfwhn1+SMDjst+tK!DNox6~-_a!_d!ah-gg|krtZlUX#HU22BQneSDJrlUdg% zTpy1JOBgI+&}bmsF*O>`+^OvSV24V3g&!>bDl{4hwll#Nd%yCs5;Zb<*hA?FGuRW> zrp;i$+7?)=VSYL0+%9?gUR>_aUb4d`(hprWQuW$W2&bdGfY_OZ+?wM(T|*EQT#_ylto{^&FHQ%gP!?v z$kW;hk%*6ZVe{Zp?ShL-F<}7%xbnfJ0IoGeR18+s^E}gD8r@%9a;UaMCfKRDq@15f z0RxxvxkP1AgG*Hls$GxOk}O+ztM)NM=hPsRN=?`!0J60O7S+}V$Q~Zlbdc>`3GSPn zlT|6Y(}T=-7k5DbWKd)+SBhhZjkp=nPYXKg@_J8*&*4T0&zx%%H@cI~aaJ8}ie0F^ z;zmY13usJLrkl>%Q6DI#t`wV{3o^-+Y;`UuJE!Zc{t8`1OA}4h%b*x2wm-%6;BNTx z{a^q7@%fL*8;?hHIb;~Fd}p~~iqt;+!`)fWttO$;WC`f{b#&z-hiY{IhtQ6q)vU)s z_pb^~y$Y6nym^{f!T8z7!~{W=g|9<(u(I%FymnEO7VwpiuZR=_zViB4yV*tOl5I%f z6mUL(R=wDgTnmV;^=K98wu4qxh^VA-f?!G@!{s; z3oeLb){grcAYQL69q{&!H?MtcTnV)Xwbp2DnOa3|lS^-gDPtOhjyG-0fRO7vS4>pc z;3_k1OoC8k9~up=hBa1I{rQ1q_A+a{+}LPr#)wVo=&LI^CMd?iW?IHEK_%EcQ-esb zDdm_*c1O(I)o85z!y0w;RZ!~))Jk}wH{P43!mT2G{nd_ToSH&dng)d`qtO1^X}_iE z7Z#*v;^j|GF>FkOVuzxbAUoh2m3Z3)$-joJv zYtrl6u;2w<2C`Wj)={tREVNur8yMhY$Bs1ERSUZgzavdGz}9j(099C!2C8a7)jl$7 z*9GZSm!YS<4qzF!qrtLsv8<-;=*>#Xywt#!*@o5V9oYt3L?tCgl1!UJ;S10>cgXX zRnEw}{1k}5s2B6K`ZGYOq_Z9OMsV9I>2zm9#WrDcEkwFfHrIL5iSP~}9Y}Ws(s7eR zx=^kt-z@2DuTqR7QC5<4veSk$Mb0UTYdWgR1nEG!7fAPo`ok^oH`KRsI5?ESLe7|N zxxVE>gr}u47M7YHpuPnQ+fjY}Z6c8FgLK>r=Hfe0ARS0o1L^i8+hIWF0kS*ECakrP zZ0mt65=0GF!CVosMVEf^?Y8*5LAero&wLS!_{sv>ZiSeV^pjBghi2B0aEgnfq5=O>-F) zDok@Q&1DW)sgDJ&R;W-H=L)SM)YcGYTZpju+Et$g3CA29a~r8LU=En80dqWW-q73S zbgP(yT@Hg&U~XT`t!s^=4n@s+<@As`i7p(xasXlU2LTWsU+?LxMX=T}2csM?R|V#B z9`4mLLufii>5~yoIf3uzIYeq$C9nls+(y_!H?C%NR+BAm2MgSE%q1uYPaMQs^aoJ~ z1(OeXu2F<;VS?MaD8-)Y1J~ey1J^3xTD=aqEy~VpJDlmmFu7O1efoC!f&TtyoLZ{W z#&TsSYQ@PkO|AHA5o(36;^P}$`|h6UjON~whBH3-{LeuvfX7;;mr-l#)j4Q|Z(lI! zEvYbP@9uRLj~NiE)K%bcXrE-&A(=3JAns{U1GnUvIL-jG_+7fVv>{d?VQRcj2X zx#rkxO}c0W;o*yVZOdvvnZ2S+17$$j-Y6q)lLBQd1!b$;3f&ZCi?OxH%5gp8&fIUG z{`&pr&(DAVh-1um%4?>{I&wdZ4VB^Q7_*(0n#+i1_a%%?-H~|>V*v@?F(=58?2DI> z0|Pm8KEg5INv<^cs_g$WapsMoEWN7#dHC?590wE98)REZvfXYTUz%>;9^NUg*cPa< z>m_&QK`~G)LNRKGDwSf?v3F3cAtEOesMR61ME;zZ1++T52GeqiRCvusR$1Gk!9HVb4Wj1ZpZ*MWCEL3Q&(Wq?rb8YR!giJL>0e^DgkgoR4r(3&4?~h$yu`WC1 z4A&9+{1gF1@5!`Mc}>FQHEvD4n&5I!jvKa}%Yi}L_c*;S^}lUGu4 zkm_Opm)>dep$DlJk&2REAk}$DH6CNC8B&dnU*F^8Qde>bkP8|M8moG86+qRlO{#%Q z`pP8%I5P+8}cC=06p7?N!^AXRuUL-n($7@XRn*|HeyOIXdlATtmlvrb5=?Fb+|6Br8SOq*Ts8)4W?aiUIwp8mJ5<+VW!L%ZWP}()$mr4y--lip$wusz3 zxHykoO9Z&K5Kz>|0$d+&#=*6B0!%`J^5dtS%T`z^5hf_Ev|f7q;F4ycUjg8P;yO;{ zH-^@TlM#KCA70Geo9Z|COcGwS6TOYGQM_>0S>p~@(e;_0jC<1YRGu&BEY)1m?G`eX zY&=+`j(A#MynTEr(O)?LzA(W%t~^Sb;2qa{@T(AMiBmp27LGSeD=oP2ixNFc(ux`8 zEP0iuJgrd7L2PX=w$j{E7qQiBD~k8gzShxauxiHF*0o|}c?S48CBCv~6}fb?KEsCQ zm%l98n2ZF_hJET-vdP66g7nJZ>@e4EMz!hccc4v1u6o1d!iLSt?jLarI?8z6?B1Rj}(g`!YNC%SGC4Q7p@_8d0_JvJ5tpA+u_hS!tmy&C4uo%4EwdY|5OB zUF&bkybQ3R;U%tXG8b3px8MKk^IxC8{rvR(%hcg4=Ny*=f9kcI4+Km=n(D0P%KND2 zJiKfa$&O7u=kK&6-CSFG&I6T<^i0u2Q=R8Q2u2QP@lF{l$g^6i|F07M73GRLtA)c^ zEJn0P7(EkhzU2EkQJcLJZLR=(S&c0kc1|qI$V`5TvWNdRHu~)0#<9HhnacVHsV1*=ge)&8Q2)oR^N1VyHb$a zF!{l({gY!#$*y;1MPwJ4bxLM!)dgFMEOl~ZRV}khAqz4K$jVbymrcfR#MiSnvW7BI zU&}-Te$~sbQe`65j~e);B7;ZpYg>LDH}gHG)invVhi3h$v4u)8w^9VQPKK>L8}y0P zawn%&)ncm@wIH@Yt!;|MHK;Yzi)I)r-nk?uED}~?JflzC6DjMo zG2Sf-Z;rm$61M6!kuKfXi5o`gjONsI8h?WszrxG{W`Nl;m_;1%fZ4V%J7`Kx61dV+ z=N^`+xTG(6@FSe8slue@+J~}s(SV-MxEW*u%wKB+G-AKj7qLpCHaar8<%a5 zVT6mLPwy$XK%2#CY5DZ$&qTY<909Qph$+Jri)<+thuH4j{1qS;>5BnkhlSXPS*g3# zTQaO~>nAfzI<1;!7?ZI8hzb~1NQgO#Gy}sF^(ur|y9}#Rh&c$cQ8gJ!p)U?G2r)ox zc(WEEmKqnaDZ@M%c4L^;+F4TYRKTz!cErH2Wekfw%Q2G$jfa0c{8`Lu@rXw1i+Dg| zoNZ(3N`g&eVo`1k%5O1z%($^HuZ?{HjG?jrLOmJxilLs|P>L;{Q48i1ZTwP>=1e^_M6gzH=3oYwGFGCpMAv zm3jiydw=nPdL>$BHB*n$TsP`byO*HeaSF~-)LWNuv4V6!o_Q1ffFL06+vPvO?|)8h zku#-&C@dP}v*{MOv(m^#b!oqXE>+;<-dp6;m*F7I6Yfy4MB7&(DAVm^NVu zYkjk!)t(9MB1&tr@xEzjHM{%^LOV3JZ0Yy62RL(a4@UTDX!2Fr|AWJsH-<8xss6u; z49{3D-v!H79Z@eE>I-7tp-#Ou} zAm@`Us4^a;gj34+MY3$lwOUY?i6DS{K;bJei=2>5f*EDn0L#CX1tUlWQf))3ey2NuR7Od!+tpNoREmdR^p;4~AM%ynE}JQ)G_PD_ zog1VAsqX%+)ijk(B9&LlH(mX4C4_IP`rEvD1yX@jdyTe5D zwby3PG|FeR*(FO8T8+2aMJS(yq6u_Ve67%Cho1rXsvBP_t)tb~iJpx2Y64%Z3;dCE zEW=Up#XDoAw`A=LeASCD)QNPRC>-oqJ*h$+EM;T8_xP7p-j22YLJvsQHmOv4@4abF zCAntHf(Pp*)ytX+&MTCJHJ5}R`#OsCn}^bgG}R@UGO|^cxsEAs+cJ@2TL&ns17({B z)+l~KnMiAODX$rwWZweHY^RLT%Z{Nnj54S)pzKwYW!>6dKrwL@WNF1^+k(t=-brUj zAe@+D5M-d(Aq1Jcpv)(tj5}ePje@Y9)7H|)^P{3{jfOr@29zBEWvUNDm3_AJ=@)}+ zXPM=?DOI+{UJ#-pfU=5F7U-1{FLmu4|GmAsQM*UoDtwwXZ%hroL=s_*L>WPkrNkb>u?tZNdV(z^+%>rBdvp zCkQY^D0Frg3+zI9xL0{7(%r3C78L6 zE`&d$lmxnNpJb=$v z;r2^n^7E@H&x@*1b)j9K<@Nnb9CW7Bnn_W1AaMJEuFW~wY2%%W^wIuVi2dl+-GQIiaODlsbG3bI}(IVlMjKq#I#l zyrUlNSrPT%s?B!8#t0P&i=HsRQYrVpJcsgArr3d7@Ao;}sks$?#kiF`VktWkwu6%G z<0^P;&3iHfa8o7)7&_4(yWE*cw&O+{@>S8d3BngeQ%Cm1vYr_dEUK>?mC^k|{jN-k ze%ksh6A0CL!c`!Y1)=(rWpvUz2n9lcP$da92&(IzqfiOB>aW!|i!07LWy4bz$tf(y zE7>v6nItEbmGer2SBrSnSg$=R|7(f8)QD?U=%Be!6|T&%$h;5?JEG=VM`FFuVGoe< zfD{TtKnjqm7E*((y6z|njoJ{kA!@@qkkyz2K2{sfIo>BHpJR@xVQBcT?*Sv>Y=wD^p2u&FiX4~_+GvRlSQybYp z+Q*wuNPDEwbGvI^RQ;Ujg1;l}o-Q$@-5Ww*L);6o5!Znt=yju)<=WDC0=+;l(5ohT z4MI#my0^lw_G!CdeNE}4a-?naqzIFo*+}S3K$z8DQ?J4w(PSH}8VA?ODW$noB64IF zlmewdsR~?N=F!D9gRIt@^@bT4i+Mg@vwj6+p;^CTZ8-IipWVo12ae_Rx->Vc)MuYZZN8%myb#0W_*kI!N9jy`Ig2r!dy`c-`4O|TurP1 z7>1Dm7yx!afDLlYKkf~kywi_))I29p>}9sy}5>3gLND^n0B26O(`iB)gc32u3) z7!Cxr44tMrVI{XQnKpISwaMZ#K%QC79Ca*PT(Yr)i0f|?FLJyx!-Sz48m)5ghl)MK-kGTWbyKGDb^mQ zlBWh2v=_hya2*X?gZxt0V`npcFCTsllyRac#co{M0Ao(M_>r?x7@KBSMw0h1>%US(yw3u*=_}i26dKqDwt;Q6 zux*w0*#2x=%&{ell-7*yKz{!=Re)ZcW&TJ7Xoe5Nb=HL+NOjJ#a(Vh%dL}8O#fyj5 zY-bd6k}`OJZ69yeyC0rieqOvA-{$SU`Ljwut8-jgj-_uv3(%?_T7&GeqeE+_CKzPb z$^?sa{Xwu-30Bnox(314i>&~+puE5>aO?I-Yv%kWTKPCazSzKHIhX?a`Jnvk7QLYg4d9s z4^mx8WrPwF31J}Bx}>6B1*sm;0a85)EKdup>0>G!r@#3qHkmMjU^jwE<5@hV0t5rWK(Lw!_94g|8wA;Z zfBo~%wiuS>U-ia$5szj)8Nve7r4xdT_!5__H7<=IK{N|782~7>r|<}feZ1M?dUsEl zEn+FoPA>19IPZj3Ud1+vit(&eYj2FgSxUof5#uCrU-I0> z3s}Tpyr=?jRT?kW@mWwtSHhPYKWYS6!6gP*MSc)~)$P6NIBet;EwfH;vw%?yHj5hI z)!16mqT6Cog8BQ37`0GBp?Qe0b-RwY>;JokY#5#UBE5|(?!J`n<^F$oJhUg zi18rD&JHg)#sDwC>-I?x0$LL=Ik%1DlLsO_{QKH zTd_|TAsYH*=##zfB}>8FUAowXaesdOarsXWr=01W(LDTa9*A-|x~)(5GhD@|0ZtfW z&y$oWI&ZAz)T|f(Ug-NcTA9_=A{r-Mq9GR-7qOJ`r@mUi+;Ur`7x@<7Xz!_ zSruT!oCB-CDzK_HRt+NSJz-4cfXg^(+Xa^hWK#X5vdX|!s+0t{ih&Com1)~X0bn8> zV76m|Yp&Y`nDJV>>Z(N9X~UT!|6%|P06Q_jKIE8ZHy=OhNc6n6p;^>?0h%4Jjx4@| z^zPUPuF&=Zu3|q3;Oh2Xb*xN!ewA7poiB90P&ih`F4)3yHYO#SUub?oe}Q2~W!NCf z1aa^v{GLzoj5{TTX_se?Im2QRJmFIbZ}@rY$(~7pS(G{TVr*rGDMfwkg=f+mVQ$f$sM^f&<>k%smC7yFcQTsg z7Voq+iUw0jZk1|#L1NWgGQux$Z}>{(msHFcf;Fx1E2vPRV?l=n`BkRn1%82FmGf&5 zV^nVI*^5Z@yVfltqy2lp?07|FasR|Pp!MvuLd$Di=>=MWRvWmz^v&&st{1vq=+;Mm z5LH%8=;+j1r#5OU6RnD%JH`L1Ix!(BKxl5)Xg4rq^qlUv0O%Sf{zP zvO3*Ws^x_e5&Wv0UxOHPl-KLzMI`!NfLRSNtFnkJ?w)w^dYznBXnCRK1zO#IrkZFq z2rW-}z0mbS*9%>*;^=}=V!77~jW0C5P%8pq$3fV;B-4z&N&3vt_b*dS>(9*lLoqGq zXl);D@0E)6zfO0wR&=_5&?8Kn&MKUiWx5x@^v8l}A8$S}?GZ=M&HVWSaiycRflU1V z?RuK~mQsvka(xy(&u1Q0TEUf$A0av!#Z>1lQ;vGHFU>N+tkKjUGoe|=3=@`m6%?C# z%unpjA1@M}8&sMr4!H$T0aVq2YLHigSR*E0&A6EEsJ0t1iQQlGX)3+f*p=vL!C*TB|7(* z5`GG_%xVBxm1D}{tKZ|GigU9HmI|;6th)b9HL+?CS=31)xGLbPfUBZ3xUk{9IHUr` z3K%P(ya2Gn0Bn$Bw{t{azW?jrKf_9ZjY2NcrDCR}{KIG}*4t4m? zEp3cuR$FEl1@7qnjGZLM0-)K)oAqu&zCg2Y^R;sRHaF;XrI%&SAzJ#8ULN!Uy(*>G zAjED*ixN>Lv~w+^jC1c~tf7Cx@Ya`wSk&GMl${f0lyx4Wxw?>4S+307Lr> z0u2B=AixGWb~}KC4jMXWThI(0G|+68W=l)ZS-TmUXlSBAqk(FzQ0+swaW;6+TB|-Y zjI_EFQhBDMK3ZkN`>N9vpFzI{Ew6N>edfFH!(y7E(}ir-Gee?6_5W48;o%?2eaRO6 zw0`m@Q4QH1p>)iIvN*7w-#OWOc5Y}HqR;`Y<@H8^%Ty6Sn>ebiC7JfL^b4wPEyP%C)UI*Gf-rOE-DJZ*qmb>54 zC$w$Avi>Vo#QQ8jo4%rLW|Y}J+WNi3ht`wW;`?pUq>6N8@sGt}q z28yxc)t>f>wwcjD`)EV$39H6I(6)+IWB=N7ahsv83g; zNpG6$0sw`fmdiy?F`*>)3lIBZ_576ijbDGPej7k#5$t%!=5h#TJ8J}|(=kdCtducr z1%e?G2n3r|X(?FC4Ar&}W_QBOa3xKA+3B3ZERqQXVFs*yeA;5dY^XE&wa#?F8R|^b znTNw!LlI|75qHyM8l@&mO_Z8|SUHF-Ei*s^V>ICO* zoO4bXX2CfWrKjLrThdJpYfwNMq+21RQE6&G+Q*yQ&p!8;Zi{8wlAS1_hn*-u4!RAx z?Uayf$lzgy<4%|Rb%A5ki&BDH?pr-y=R56KKv@K2!&85j*@5e<)XGt~xm>UFUi7yN zNs!r2FvU2vVs!yocvWZ$k*|R)`jN9Bi~k@1vRyVkm#z?4uDd0 zcGbbIL4?`iCJI0X5eASQ24oFQ0baj>?tw4pFyIULIv~CVDdvWL*zbRTPU6`S4Ye|z zXFR;GwanF)e^v}=qU-S>DLw?CnNE7)O~u|Jt+_ee6P;Ds``WlS_3Eb9+!YjF zB>4NCdplcm!M!c^57y^iaya+{4A@3jnIV9gnMg3F)XEm zd(ebi)xy)u2-p8BUPdZ9!!aelgl0{Z2hecL^5rVJ`7$FGDg7Iu4V0n!wk+T;~ zwdqMmfo^aD6QJYK*VM#oG0+Wv)9U8{b;qrgCbF+Tg$!}|vaDB%j6dI!bKx&mT5&-h zF??+pvi+rI576yi`93b3?RO3^v(lx7Ta1@uLC4t*Zr5lMIeY6Aa62R1A`2LR+aXn4 zDY$I`GFr9Wlg+%TfHvXmZ=ZgA4RHDN*VjKjg%6pcoC?3y-cpoP3n>@>xFWTx6xK0v zk8BQeuav5jRj{5W=(dIy$#j5KlyXc+GF3@lt6=2~^|9)b*W|Tgxad5h+{2rXdu3Y< zFCL4`*K5^$3D=OxHIQW65!#7)3D^>9~Pn zpjaZszW&$-ZJuQaH0ixGWEt87+EnMcF*b50upHW0XT0Q!MDF%za}wMZnEod zHn|E5O-MArty#FOK{1YY*LCP8LA1i$&NeCWOXhy-kDGnTk^7d8aJ*b&-#90ej}V{+yHH$ZT{ko=_8zHSiC{TsH89qa?wmBPpl6DZAY9=S61Pe z?Rv~+Nf5E4jdj$9ZjSTO#{QU1%vvW@c8^;9UL!K>a#79!8*V$*^3xRyRWK_4 zmshcwySeAZW~?Mpee#Pptcc2x6(My66w*#8cIXo|@^iX7v=BDMP$2;tRV2L$ z?9xuo0UBrnZP^1frKSgHCs@Rxt@MH2C{%Y*9TbCNqBzC2Tf|}K^m7zn8SqMzVTcq6Pu#Za85|9h>s1qB(_$!Uq;i z-5O*0EiwXapsirqwi;Lvb9S^9el};S`VM4%mmjcRKZykOe6+1Gu(1DGoSsvLXqJVT zVyRPAkcx;Mbq8KY3F=Cu?vQ73N)dKeUa7NWHOJ*4*jWkc4niHDvp75NzJ$W>9F-J* z`x(-PiYjOWZF8GN=A6Y9zVfZTtB>RS17q5dpn|sCQ(WYyuRscw)j<_JW*-&P+6ZpP zY(t;?;+TDXfEVA`2E|e+wxwcC8RFZ1Cu(-qiCWodC&k};Z_~2^=Z(#9Njy|z9-ApO z=N{$a>jJFrvSK-5kHa%4mR&J&)3+UkD#imcw_+P+n6`=;lL*{mu?;iK_|CS`sZ^GT z3=@jw$uKut>^42^EdDT!Dkq&_*T{jxG*EYN>b4Hp!kqo#g^z}+7_bRX+&1rKUH}5c zpjZ)|w_;wMpy;a-OeK}on%%+=&hu2D4%G2%PopqreyVWVbUh2JL~>4v0c|YTxPdm% zmOU3$Vpa+C6pC>>QDeQ!10_%>mR&LOvu4?aFrnPF9aP^AFZu12spxUUD=^rd~*lMuBL!c4&J?gOj+LTQE2dQ(nsiK z3{_pAD|ZUsR8bQZ7*-mB7e0MYDxfP8x-*&fFj%@;4Cm&=aD{ibnTn((%We4@R!D+2 z&^DhonsrkZN%G~kyv3+Zx{$s*fWdJbhDbqM_wZP>?Yvh~&n+@2RkUi>X~nR9+W&O+ zfDPjaP;JR*{LBFEj~^RV9sHd8Tnayo)AhDGC)s7JIE({;hXIgJ#v0~bRTX5xv7N3Q z1*zFJkZ>-j;xG=hF{RC67syT8s*Q8}dPPuY0Bwh-ZDt$XX5P&yJ{0dA>Z&-VgLmDF z9y^qyFqt-2SxZKhJLV9xR3)5em!;;tD;`-YXgeZpde$=D@{6m+d&e$I4vZ2`0$ zy0|)XWpC*f2qUaz2q51I1ONnpJYzdG}j$g;arhI8Kt(pJ#MmA2VCmCB{f zR9+d&?+zKxv>|E(ZCl(SvsndVPM=&e*;Q@@!c>xkvg|4loUdenckqtgWS3IYqqwuw zRoPuMgSskIJGN?!TI6u^=}i=e7CA(5@UDB&V;{)5d4pVMpp9Lv0o}i#4YbYAQm2WfI8&C&vO@&4fi{-3bxVK4o0jz*T=RN?3%@Z{k@JFGQI+%hxRK+yRPqYwR)CIv zlMCR>ejK;huYdl_KWZyeu^LUky53d6Dr2cw!fYkIG=g0k%9*f{6E+YA!fwByuRoW% z_GH+|l)`k^e5FEAw%p@2T?J{aW0!;GolHCf9D9k8V?x|V) zSfdTUdt*2^Z{XAngk>g7qe(+t5B{xpGO)WO2BQQZtPsNf`|F>7?sP&`iLgf&NGED6IA83U65~MLA?EnX6cFq`6;XNAhi_v5(!viz) zXuvhNHfx>6^x38}tkWQ46g$^AuhT$REnMR`c5dh7p~WAqSrWvtz{xjWLmqlU*LWYT z$ypnM%I-z0-)KaJ-G(59Y9NgjX{-vK#?U9fCRqH7 z4OtNs2m@ivPtoRuW)$Y#!!B&fph7}3sz`bh*kzlXLo;0a1lMx6(@sAyJHdJlZKY?s zUW4ZyC|ztN<~^I;W~26%jk<4!i;c@mq<`9q;^2=~*`$m%f0~lGPpyDED#>3nl*S>xR!f!9jy(0@{9BH^`TvSSDWRfQM7!3uyMkgy2H1fqB1*YN}=#e@C)|mg!vPwcJA9}X=8O>(zH3s& zP)&H^wnLZoS`RF017SyKua$Fl>K1<8fvKdjTC-c@!Pz;Cfx)8NdAnaAb{?CVR z@u&GA{x-dt7?OoKS)~@T{sk`8GgJ9MQnB+)R5mE7S@xhIB-_NZ>z1)^K(6`onM>2e zoT1I=nxt~CrHZau!*Cd#bE=7Z+~FEW*TSqNX{E<&L3mB+f5ZIQGsD;A$Z}__nYQc4l`4mC|4?I%Qp|ry%&B?^j3Hmo2f`zvfP%b9eMK* zT!U*_T%$Sh8j&PlZfjeN*Q5*S{r?P(;EveZ2G`)47}({p0cl#yn|YT?6-}CTS~0Aa z_CK9HR0C-s?S+~5lyiUl_`HvP%Fpf;lEoKgA6<$g; z-aB^5Dd$ouiV1M-u%*=K8A=W}QJoRiGSm+x2k3VY{dh!m%FkZ^R&tAob&4#zOJO*- zh`=?t#?rOf6ZYl0W-70Y<#!3pfn7r>6|UX6mbH{hbAq^LaI4&sgQ+A5W!aS+I1g^& zXb#x%8r)KP`b{xssiJbbZ#i=n^`#kmkOtED#BUBa``yHEXoW**71(tza^yp}d2?E4 zaE)DY0j|L{xHdC6o!$(B z#_=`ZqEVidGil#s4CAd4J#m|;tZ>fiv~Qv^)`s{m$pYVcpB(kA_sN{TUA~>xw_9v} z-h=kw+ceMUH+u9<-aV}zUR2&eGsCyruO;bQwCR8EKJSjV=Ra*<;hDW;%dXOs0JD_{ z*2`qOZXtka@@CQ6x150W_wr^3EcLkn?8tyMpQd*Mt$sEAnr~gSrrKytAzDMUhG^|T z*e%XRb46=2BD0y(Rmvb#yM}6ude;!DL9bKjwN|LMJSo<9!LGM!j3=je^oB|byKhf=KKkTeg6lhnfM=BqX=i)E656O{){haGQ zV+-eW5R@Ki%DeA~JUQ9`x}i5AoP%>miPmV&b;7VOOlX!11WJ~^|bOE=+nUq|lrI>8B?YC3Y^)Lu>@4V@mI(YPr5TX5HH zx9!~n-ld?v>A$7v%X3eVg4X@F>6-@1G%j-r3geEKg1(k=Zg8)Grs;cfPe2%&Z$TIc z>t1B1OI+4>t&7t_+j%=@qet|?lY((zM_x~E) z{B3P~hf&J5?n^lE=5Hw~X*|jO(@v_S4k4XanuhZTg-)8gt!*{+KIIcN&zj&x%8F+7 zw^6}6m9{PZq_sC9wHSOu+j^hmxBC0x9X`5X?YBYbp0WA;ehqjphIh-rpIGbRuV>Ta zuLsH>v3}kInTW-L8E49n);@VbK_N^Dr_8d;q_$*ARxw=BIvJsXsUn$rej9VSOzO28 z2`2ACAlrHO3aLI!?&;`0cSC~MTGfo$q9fm5z z9GUiSe;uWZM5GJJA?WHJg0367>;$^3mQocCWvDZPEQDJtg=VyO*XhssZKb@8gfOKGxhCl99v+UanI+2A4$@59oiEUx-*o3^Sb>d`H0V}P5i_+W zcC3Q=JQDRBfa##nn$Np6AIal+j2ex=7EedAiohlZRs-|heoAvI`ttMj{u*^9rTU%qX`pVFF&Mtn5l!!P*t zdL#a;P4~;F3vCzvgix*Q12S~sgE~-mtb8-NHK8C`x?8MU@ZlER>K-1|tyDpBX}kUC z1(R01sVJL@5!pXK|L3pIfBoC%0p4`wh@_d_CnkEIeDmj0ay!|^<)+`x&kl&2EY~pmJH92=yXtveIO9{Ff{#Tc~sp!ldj{&A);{*w7=Jtb&!+_6EDQJgVyUkxK;0D})+aYi}XoR*dWaD9*?!vaO1G0rmX(dUQDx0o|jG#rU z7whCLNS8jJJF0BDGA@Mp(&>aW!lw<{6nv_l_a~u+Om)}l=2XE7a;d-hHQIVVlWj(N z1)?t_J!LSrUfgE7xDC2Nw^emZov1;#t#mtRG`C^k)-D%P`qPKM{`mFl^N*iHzIi2d z%-a&ab$`LkYC`{WIra+$>u8I`*@ODM>|1|A{BmAOQuvS}s>w^0)Fz}pS=IlWqzKlg zGS4;9&)1JaOejtJ$<&AJDA*SZD^#pczBWWUJY<5qV^%?Z-khXqH>EY^QIV zW~b|BqNCMScbR6Gw)Y6ZT>G)$5B!~$KW^jm9c7o0#CRK)Kn(E$Mgoby2P*ASSfU}|CViQud1X~ zA-NQs>PF}wg%zo#;7uotw((zwly=RIbdp}`)aPPlxajlj_SxF6=d^6XIotiy&z{{P zvdPNu+pl_$!?)|$0-S?$aIW_U!P2=@+&1nWH|Mrq7~<@8VMuAR%}7XX6~8?bHn^** zexG|;D{v&(LKR4nPw~?<_L}$`qk9rk6_b0dOiw~8D>5qBMG0x|SIiZ*-tXpAqYEnr zaPtyXcik5P+h7}PV{h9*58Gx3ZG(=D2W`F!+TzzdH9M75Nufk~F%nzox(}a_p4!R_ zMa#+xBhpc2ln?G`>1j+MwOf>)#-F?Q%BDWInXeC4OPdt(BhMeyP9I z!x5pn^NiQAT#6;dSVqX0jn1OS~C*Z6sY7 zD@}5+{pD&zy_qN-NwO7B$DfB(K9!tOyMBturPONo4`?Tov-(XJNiE*8E|T7__*CK@ z?$Bpc`~MyKYI%>Q8z=t6#TyWQo(0B|}Bh3co=g8pr}!Q_1Rk z|3282dOIgPdp6{OuU_+(BKGL*y=hNeC3L;x^y$D}wFmb0^j_h>9$qo11+F+B(yxxYLDu-SfBy4fL8fvO+BjL;#hu&HV4{pP7m5Z+sg4+;+%dJ zugvMa!XZ7pV&E0aL;BQO*cDFg6Z#IH(Cat*_EFb)l(U%Sd+3M z$yPrwSSwGiCBC(rwxpV>{ao6YVe zGjq-TZYRl7VuFK;UGZ?9a3GyE9av}IdUR1>)}$D48u3x4i*sC|rdva9t^4rPF4+$U zp=f25Z;V2)nTnxu*UKjtIXkyvdG-AIr)-Wtvp}sEtG19Y7d;kx4XHoUo=;UP9(M6l zX;@_(hLCp0nDyRI>daWnkNv`|R zuqCfZ?RM8iA2oyS*WpS6)&up%)A*lMPT(l0-^vbAeV5k2I}xMB-oz6-360lpwT zHS8T6W(a%`=wKiBJGBs7#6Zox!#Yz$j7NLCL^o;CZ06LnK=T)7r9;-E%ZQl1*bj}K z&?pT&fm8Dxwai9*USaJ_zb^uN8f0hU1Ap57*}I`(8+ZzN_HGrEefGt-Xbhh14$p9c z9flB@0>7m4o3Ko&Oy{dlngGA&F}~;flxn>qY$2}m-Mi=Rm4tEU$^A}x>0`?i98nig zgH|m#j~abLKDy4H+`lYrm4f7y5#o07=G81SsVPr;7f<*dzLXD7{6{i?i1%=Zs`DY& zX+7`DY;x#-Qq)H}z?U$hWHkeM9_xxZuVc8EFazMAxNeEF1D5xT2TTTm5TWzUGzupI zHTDj|=7pswp%fITpf@O(4Gl0~`)M!hgbFb!Isp!0s^Uy@T!HtnznHBP#Yoq$5t9F+ z^eb;!>MHlucY0K!h#2CaiZkaHLOp0@EKpF}2=GCNR9(Zt+V5aWdw{@QyW2F-mv~hP z1#Zq6h=ejqefd!k-<7I4r;%G7!(7XYu;1MODLM8#ks!8kZPeSyO)fu5 znMmOSPpjs4^k*-04L1Ko&qfH6b^J$Q*I4x$m@$+__0=ev16BSy7%VZa~d&eva&71`caZR|+j&aM)eNbjmoAx$T5QPs*1xkCP5; zp_6J4nZ~UUBITTzw8~9VY?AW{1?jbS<(BF35aBlKeVCKqdd=c;TTRLA-pR2dIb0-sg6Y^uUK3&z+IOK zuw3Y5#X#?=|9q6ORL_dPIn07P64PpFG|X5R{5L|tIXf9sefFN{9d6fmFuS3sz?F*{ z*s#-wj{Ra>m)j5F4Et>Dw8{QppJEV>c%6Rr|D#!q@to%GCJ`l@h7K9FcT_v;RxNUg z-`nuSpTBff2dlAqlY6-P@o0<2IrLB+-R>qTOoDjXJMTAB>$8mYmx!peFB{)bBUmAC zes=BzjQ)$#@3hT^k&gdS9UUn4y6-AZ z5ge;1>+Z-BGY`iTWC-J8{t?1b;E=&?8)dcg-;^>;13c}Lub67i``SD^0GwLJVqZQe z5hwr4Ih(fTavsl;^iA5=2NFvq7r_hXsPq^F(rPVNyG`WIQ;8_^-Qoi|lgSm|U+TJ@ z?f()1R#7b7l+jS7Wllgsh4O zybA1O=muC#HDIIc7>ss`w1lI*F*heA@?Ow-P`C*m$d1_9Mx{te8m!++Kl7%iIDRwS zY;MGEo=Z$e#_uy{)Hi@rk~HiWNa>m39UdOp>~?ewGa_e<>v19btl3qj1v`Z1#Bes2 zjgfH@wivAZ7q)HszU_?OueMh;%&z2T&OK@R@8l+5Ol0g9t1R&dp7u2-x2EZz@ZqAU zdAOK#>4Odr&q%jh{)^lrILXC= zU55suCc=}%TZgThAx?&Fs1g2~RycSI8Lx4c%E&*wvP?ICc#P0-DP#0YEtiDFCq`7y zUd`~IR|x7%g-o>N5;mIA;&_BlhP-KOq-hcRm)(XYKiM@@=eAU3)$;9qO<%Kx5#X-B zCKGNjBuHctMJQWkhxNugV0n){pJUwihCI3@5u)arT**!#u%}ByG<+w~Mq=UKc;0Zs zZ13fQfp_26-x-cdxgYkwK+btV*T6uBbMJ}IPFQ_^o+3F4#2MY?7Zhyv3_*%>Gl_ z+80Q1@lF(Fd%5iv1yg-DpCHnyjquPX&{{Hc-y5zl2 zYO|I0KEPOj-OljL%8VqOzcMtbX`caeg&qwgz5G7`esRMDn|+Khy{bpe={cK@fR+Ki zoSk&2(+_7wFm$fAFuQP~9A~~73?YJL$NYc?^90;pVWE-V!X`>(Ii3k6;boCj%Jgw8 z2h}=h=m-syKlOdp5UP;w#PG#=$y*COks(pOS; z+*dEt$^?5xdL{HYD*Unk!rI*f59HV{l)sg?Aoa#`fs0tEH;eg60M}x{8y�ks}zJ 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 literal 0 HcmV?d00001 From 1e3e445e3f06c7f25edd1c6150a8e39e0db07249 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Fri, 17 Apr 2015 18:53:46 +0700 Subject: [PATCH 24/53] Use cross-platform mmap() wrapper in CompactPT. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MmapAllocator header made use of sys/mman.h and mmap(), which are Unix-specific. But util has a wrapper which also works on Windows. This also fixes the error handling: when mmap() failed, the old code would return an invalid (but non-NULL!) pointer — leading to a crash. The wrapper will throw an exception with a helpful error message. --- .../CompactPT/MmapAllocator.h | 40 +++++++++++-------- util/mmap.hh | 5 ++- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/moses/TranslationModel/CompactPT/MmapAllocator.h b/moses/TranslationModel/CompactPT/MmapAllocator.h index bf08574ff..389b60359 100644 --- a/moses/TranslationModel/CompactPT/MmapAllocator.h +++ b/moses/TranslationModel/CompactPT/MmapAllocator.h @@ -24,14 +24,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include #include -#include #include #include -#ifndef __MMAN_PAGE_SIZE__ -#define __MMAN_PAGE_SIZE__ sysconf(_SC_PAGE_SIZE) +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#else +#include #endif +#include "util/mmap.hh" + namespace Moses { template @@ -60,25 +64,25 @@ public: MmapAllocator() throw() : m_file_ptr(std::tmpfile()), m_file_desc(fileno(m_file_ptr)), - m_page_size(__MMAN_PAGE_SIZE__), m_map_size(0), m_data_ptr(0), + m_page_size(util::SizePage()), m_map_size(0), m_data_ptr(0), m_data_offset(0), m_fixed(false), m_count(new size_t(0)) { } MmapAllocator(std::FILE* f_ptr) throw() : m_file_ptr(f_ptr), m_file_desc(fileno(m_file_ptr)), - m_page_size(__MMAN_PAGE_SIZE__), m_map_size(0), m_data_ptr(0), + m_page_size(util::SizePage()), m_map_size(0), m_data_ptr(0), m_data_offset(0), m_fixed(false), m_count(new size_t(0)) { } MmapAllocator(std::FILE* f_ptr, size_t data_offset) throw() : m_file_ptr(f_ptr), m_file_desc(fileno(m_file_ptr)), - m_page_size(__MMAN_PAGE_SIZE__), m_map_size(0), m_data_ptr(0), + m_page_size(util::SizePage()), m_map_size(0), m_data_ptr(0), m_data_offset(data_offset), m_fixed(true), m_count(new size_t(0)) { } MmapAllocator(std::string fileName) throw() : m_file_ptr(std::fopen(fileName.c_str(), "wb+")), m_file_desc(fileno(m_file_ptr)), - m_page_size(__MMAN_PAGE_SIZE__), m_map_size(0), m_data_ptr(0), + m_page_size(util::SizePage()), m_map_size(0), m_data_ptr(0), m_data_offset(0), m_fixed(false), m_count(new size_t(0)) { } @@ -92,7 +96,7 @@ public: ~MmapAllocator() throw() { if(m_data_ptr && *m_count == 0) { - munmap(m_data_ptr, m_map_size); + util::UnmapOrThrow(m_data_ptr, m_map_size); if(!m_fixed && std::ftell(m_file_ptr) != -1) std::fclose(m_file_ptr); } @@ -119,13 +123,17 @@ public: pointer allocate (size_type num, const void* = 0) { m_map_size = num * sizeof(T); +#if defined(_WIN32) || defined(_WIN64) + // On Windows, MAP_SHARED is not defined and MapOrThrow ignores the flags. + const int map_shared = 0; +#else + const int map_shared = MAP_SHARED; +#endif if(!m_fixed) { size_t read = 0; read += ftruncate(m_file_desc, m_map_size); - m_data_ptr = (char*)mmap(0, m_map_size, PROT_READ|PROT_WRITE, MAP_SHARED, - m_file_desc, 0); - if(m_data_ptr == MAP_FAILED) - std::cerr << "Error: mmapping" << std::endl; + m_data_ptr = (char *)util::MapOrThrow( + m_map_size, true, map_shared, false, m_file_desc, 0); return (pointer)m_data_ptr; } else { size_t map_offset = (m_data_offset / m_page_size) * m_page_size; @@ -133,8 +141,8 @@ public: size_t map_size = m_map_size + relative_offset; - m_data_ptr = (char*)mmap(0, map_size, PROT_READ, MAP_SHARED, - m_file_desc, map_offset); + m_data_ptr = (char *)util::MapOrThrow( + m_map_size, false, map_shared, false, m_file_desc, map_offset); return (pointer)(m_data_ptr + relative_offset); } @@ -142,11 +150,11 @@ public: void deallocate (pointer p, size_type num) { if(!m_fixed) { - munmap(p, num * sizeof(T)); + util::UnmapOrThrow(p, num * sizeof(T)); } else { size_t map_offset = (m_data_offset / m_page_size) * m_page_size; size_t relative_offset = m_data_offset - map_offset; - munmap((pointer)((char*)p - relative_offset), num * sizeof(T)); + util::UnmapOrThrow((pointer)((char*)p - relative_offset), num * sizeof(T)); } } diff --git a/util/mmap.hh b/util/mmap.hh index 9b1e120f3..37feb5bee 100644 --- a/util/mmap.hh +++ b/util/mmap.hh @@ -100,9 +100,12 @@ typedef enum { extern const int kFileFlags; -// Wrapper around mmap to check it worked and hide some platform macros. +// Cross-platform, error-checking wrapper for mmap(). void *MapOrThrow(std::size_t size, bool for_write, int flags, bool prefault, int fd, uint64_t offset = 0); +// Cross-platform, error-checking wrapper for munmap(). +void UnmapOrThrow(void *start, size_t length); + void MapRead(LoadMethod method, int fd, uint64_t offset, std::size_t size, scoped_memory &out); void MapAnonymous(std::size_t size, scoped_memory &to); From c5e9a58ae2acb70d05a59722989b526fcce31e9f Mon Sep 17 00:00:00 2001 From: Barry Haddow Date: Fri, 17 Apr 2015 16:17:12 +0100 Subject: [PATCH 25/53] Hypergraph output shouldn't crash when nbest list in current directory --- moses/HypergraphOutput.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/moses/HypergraphOutput.cpp b/moses/HypergraphOutput.cpp index 47c564882..6b353a83b 100644 --- a/moses/HypergraphOutput.cpp +++ b/moses/HypergraphOutput.cpp @@ -98,6 +98,7 @@ HypergraphOutput::HypergraphOutput(size_t precision) : // If this line gives you compile errors, // contact Lane Schwartz on the Moses mailing list m_hypergraphDir = nbestPath.parent_path().string(); + if (m_hypergraphDir.empty()) m_hypergraphDir="."; } else { stringstream hypergraphDirName; From d56f317f2ee844beffc7f83e3e6ae2aeb80add61 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Fri, 17 Apr 2015 22:57:55 +0700 Subject: [PATCH 26/53] New helper classes: temp_dir & temp_file. I'm adding these because boost::filesystem::unique_path introduces encoding issues: on Windows the path is in wchar_t, breaking use of those strings in various places! Encoding the strings is just too much work. It's still possible that the current temp_file implementation won't build on Windows (it uses POSIX mkstemp() and close()) but that can be fixed underneath the API. --- .../PhraseDictionaryTransliteration.cpp | 19 ++- util/Jamfile | 2 +- util/tempfile.hh | 79 ++++++++++++ util/tempfile_test.cc | 119 ++++++++++++++++++ 4 files changed, 206 insertions(+), 13 deletions(-) create mode 100644 util/tempfile.hh create mode 100644 util/tempfile_test.cc diff --git a/moses/TranslationModel/PhraseDictionaryTransliteration.cpp b/moses/TranslationModel/PhraseDictionaryTransliteration.cpp index 69b7e9f5f..1d654f4b0 100644 --- a/moses/TranslationModel/PhraseDictionaryTransliteration.cpp +++ b/moses/TranslationModel/PhraseDictionaryTransliteration.cpp @@ -1,11 +1,11 @@ // vim:tabstop=2 #include -#include #include "PhraseDictionaryTransliteration.h" #include "moses/TranslationModel/CYKPlusParser/ChartRuleLookupManagerSkeleton.h" #include "moses/DecodeGraph.h" #include "moses/DecodeStep.h" +#include "util/tempfile.hh" using namespace std; @@ -70,11 +70,10 @@ void PhraseDictionaryTransliteration::GetTargetPhraseCollection(InputPath &input inputPath.SetTargetPhrases(*this, tpColl, NULL); } else { // TRANSLITERATE - const boost::filesystem::path - inFile = boost::filesystem::unique_path(), - outDir = boost::filesystem::unique_path(); + const util::temp_file inFile; + const util::temp_dir outDir; - ofstream inStream(inFile.c_str()); + ofstream inStream(inFile.path().c_str()); inStream << sourcePhrase.ToString() << endl; inStream.close(); @@ -84,14 +83,14 @@ void PhraseDictionaryTransliteration::GetTargetPhraseCollection(InputPath &input " --external-bin-dir " + m_externalDir + " --input-extension " + m_inputLang + " --output-extension " + m_outputLang + - " --oov-file " + inFile.native() + - " --out-dir " + outDir.native(); + " --oov-file " + inFile.path() + + " --out-dir " + outDir.path(); int ret = system(cmd.c_str()); UTIL_THROW_IF2(ret != 0, "Transliteration script error"); TargetPhraseCollection *tpColl = new TargetPhraseCollection(); - vector targetPhrases = CreateTargetPhrases(sourcePhrase, outDir.native()); + vector targetPhrases = CreateTargetPhrases(sourcePhrase, outDir.path()); vector::const_iterator iter; for (iter = targetPhrases.begin(); iter != targetPhrases.end(); ++iter) { TargetPhrase *tp = *iter; @@ -102,10 +101,6 @@ void PhraseDictionaryTransliteration::GetTargetPhraseCollection(InputPath &input cache[hash] = value; inputPath.SetTargetPhrases(*this, tpColl, NULL); - - // clean up temporary files - remove(inFile.c_str()); - boost::filesystem::remove_all(outDir); } } diff --git a/util/Jamfile b/util/Jamfile index 18b20a33a..a82a5e23d 100644 --- a/util/Jamfile +++ b/util/Jamfile @@ -32,5 +32,5 @@ import testing ; run file_piece_test.o kenutil /top//boost_unit_test_framework : : file_piece.cc ; for local t in [ glob *_test.cc : file_piece_test.cc read_compressed_test.cc ] { local name = [ MATCH "(.*)\.cc" : $(t) ] ; - unit-test $(name) : $(t) kenutil /top//boost_unit_test_framework /top//boost_system ; + unit-test $(name) : $(t) kenutil /top//boost_unit_test_framework /top//boost_filesystem /top//boost_system ; } diff --git a/util/tempfile.hh b/util/tempfile.hh new file mode 100644 index 000000000..228238823 --- /dev/null +++ b/util/tempfile.hh @@ -0,0 +1,79 @@ +#ifndef UTIL_TEMPFILE_H +#define UTIL_TEMPFILE_H + +// Utilities for creating temporary files and directories. + +#include +#include +#include + +#include +#include + +#include "util/exception.hh" + +namespace util +{ + +/** Temporary directory. + * + * Automatically creates, and on destruction deletes, a temporary directory. + * The actual directory in the filesystem will only exist while the temp_dir + * object exists. + * + * If the directory no longer exists by the time the temp_dir is destroyed, + * no cleanup happens. + */ +class temp_dir : boost::noncopyable +{ +public: + temp_dir() + { + char buf[] = "tmpdir.XXXXXX"; + m_path = std::string(mkdtemp(buf)); + } + + ~temp_dir() + { + boost::filesystem::remove_all(path()); + } + + /// Return the temporary directory's full path. + const std::string &path() const { return m_path; } + +private: + std::string m_path; +}; + + +/** Temporary file. + * + * Automatically creates, and on destruction deletes, a temporary file. + */ +class temp_file : boost::noncopyable +{ +public: + temp_file() + { + char buf[] = "tmp.XXXXXX"; + const int fd = mkstemp(buf); + if (fd == -1) throw ErrnoException(); + close(fd); + m_path = buf; + } + + ~temp_file() + { + boost::filesystem::remove(path()); + } + + /// Return the temporary file's full path. + const std::string &path() const { return m_path; } + +private: + std::string m_path; +}; + +} // namespace util + +#endif diff --git a/util/tempfile_test.cc b/util/tempfile_test.cc new file mode 100644 index 000000000..49736fe0c --- /dev/null +++ b/util/tempfile_test.cc @@ -0,0 +1,119 @@ +#include "util/tempfile.hh" + +#include + +#include + +#define BOOST_TEST_MODULE TempFileTest +#include + +namespace util +{ +namespace +{ + +BOOST_AUTO_TEST_CASE(temp_dir_has_path) +{ + BOOST_CHECK(temp_dir().path().size() > 0); +} + +BOOST_AUTO_TEST_CASE(temp_dir_creates_temp_directory) +{ + const temp_dir t; + BOOST_CHECK(boost::filesystem::exists(t.path())); + BOOST_CHECK(boost::filesystem::is_directory(t.path())); +} + +BOOST_AUTO_TEST_CASE(temp_dir_creates_unique_directory) +{ + BOOST_CHECK(temp_dir().path() != temp_dir().path()); +} + +BOOST_AUTO_TEST_CASE(temp_dir_cleans_up_directory) +{ + std::string path; + { + const temp_dir t; + path = t.path(); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +BOOST_AUTO_TEST_CASE(temp_dir_cleanup_succeeds_if_directory_contains_file) +{ + std::string path; + { + const temp_dir t; + path = t.path(); + boost::filesystem::create_directory(path + "/directory"); + std::ofstream file((path + "/file").c_str()); + file << "Text"; + file.flush(); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +BOOST_AUTO_TEST_CASE(temp_dir_cleanup_succeeds_if_directory_is_gone) +{ + std::string path; + { + const temp_dir t; + path = t.path(); + boost::filesystem::remove_all(path); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +BOOST_AUTO_TEST_CASE(temp_file_has_path) +{ + BOOST_CHECK(temp_file().path().size() > 0); +} + +BOOST_AUTO_TEST_CASE(temp_file_creates_temp_file) +{ + const temp_file f; + BOOST_CHECK(boost::filesystem::exists(f.path())); + BOOST_CHECK(boost::filesystem::is_regular_file(f.path())); +} + +BOOST_AUTO_TEST_CASE(temp_file_creates_unique_file) +{ + BOOST_CHECK(temp_file().path() != temp_file().path()); +} + +BOOST_AUTO_TEST_CASE(temp_file_creates_writable_file) +{ + const std::string data = "Test-data-goes-here"; + const temp_file f; + std::ofstream outfile(f.path().c_str()); + outfile << data; + outfile.flush(); + std::string read_data; + std::ifstream infile(f.path().c_str()); + infile >> read_data; + BOOST_CHECK_EQUAL(data, read_data); +} + +BOOST_AUTO_TEST_CASE(temp_file_cleans_up_file) +{ + std::string path; + { + const temp_file f; + path = f.path(); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +BOOST_AUTO_TEST_CASE(temp_file_cleanup_succeeds_if_file_is_gone) +{ + std::string path; + { + const temp_file t; + path = t.path(); + boost::filesystem::remove(path); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +} // namespace anonymous +} // namespace util From abfdb61bc9c3281de2e5ef26df30634ed2c84d89 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Sat, 18 Apr 2015 00:21:18 +0700 Subject: [PATCH 27/53] Cross-platform tempfile implementation. This makes temp_file and temp_dir work both on POSIX-like platforms and on Windows. It also fixes a bug where the temporary files/directories were created in the current working directory, instead of in the system's standard location for temporary files. Unfortunately the Windows and POSIX code diverge quite a bit on that point. --- util/tempfile.hh | 78 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/util/tempfile.hh b/util/tempfile.hh index 228238823..9b872a27e 100644 --- a/util/tempfile.hh +++ b/util/tempfile.hh @@ -3,18 +3,74 @@ // Utilities for creating temporary files and directories. +#include #include #include #include +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + #include #include #include "util/exception.hh" +#include "util/unistd.hh" namespace util { +/// Obtain a directory for temporary files, e.g. /tmp. +std::string temp_location() +{ +#if defined(_WIN32) || defined(_WIN64) + char dir_buffer[1000]; + if (GetTempPath(1000, dir_buffer) == 0) + throw std::runtime_error("Could not read temporary directory."); + return std::string(dir_buffer); +#else + // POSIX says to try these environment variables, in this order: + const char *const vars[] = {"TMPDIR", "TMP", "TEMPDIR", "TEMP", 0}; + for (int i=0; vars[i]; ++i) + { + const char *val = getenv(vars[i]); + // Environment variable is set and nonempty. Use it. + if (val && *val) return val; + } + // No environment variables set. Default to /tmp. + return "/tmp"; +#endif +} + + +#if defined(_WIN32) || defined(_WIN64) +/// Windows helper: create temporary filename. +std::string windows_tmpnam() +{ + const std::string tmp = temp_location(); + char output_buffer[MAX_PATH]; + if (GetTempFileName(tmp.c_str(), "tmp", 0, output_buffer) == 0) + throw std::runtime_error("Could not create temporary file name."); + return output_buffer; +} +#else +/** POSIX helper: create template for temporary filename. + * + * Writes the template into buf, which must have room for at least PATH_MAX + * bytes. The function fails if the template is too long. + */ +void posix_tmp_template(char *buf) +{ + const std::string tmp = temp_location(); + const std::string name_template = tmp + "/tmp.XXXXXX"; + if (name_template.size() >= PATH_MAX-1) + throw std::runtime_error("Path for temp files is too long: " + tmp); + strcpy(buf, name_template.c_str()); +} +#endif + + /** Temporary directory. * * Automatically creates, and on destruction deletes, a temporary directory. @@ -22,15 +78,21 @@ namespace util * object exists. * * If the directory no longer exists by the time the temp_dir is destroyed, - * no cleanup happens. + * cleanup is skipped. */ class temp_dir : boost::noncopyable { public: temp_dir() { - char buf[] = "tmpdir.XXXXXX"; +#if defined(_WIN32) || defined(_WIN64) + m_path = windows_tmpnam(); + boost::filesystem::create_directory(m_path); +#else + char buf[PATH_MAX]; + posix_tmp_template(buf); m_path = std::string(mkdtemp(buf)); +#endif } ~temp_dir() @@ -49,17 +111,27 @@ private: /** Temporary file. * * Automatically creates, and on destruction deletes, a temporary file. + * + * If the file no longer exists by the time the temp_file is destroyed, + * cleanup is skipped. */ class temp_file : boost::noncopyable { public: temp_file() { - char buf[] = "tmp.XXXXXX"; +#if defined(_WIN32) || defined(_WIN64) + m_path = windows_tmpnam(); + std::ofstream out(m_path.c_str()); + out.flush(); +#else + char buf[PATH_MAX]; + posix_tmp_template(buf); const int fd = mkstemp(buf); if (fd == -1) throw ErrnoException(); close(fd); m_path = buf; +#endif } ~temp_file() From 4647986a12aebc1a8df305634ff0154e2b13446d Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Sat, 18 Apr 2015 00:59:40 +0700 Subject: [PATCH 28/53] Include winsock2.h on Windows. This makes socket code build successfully on Windows. --- moses/LM/Remote.cpp | 12 ++++++++---- moses/LM/Remote.h | 8 +++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/moses/LM/Remote.cpp b/moses/LM/Remote.cpp index 539809324..33946442a 100644 --- a/moses/LM/Remote.cpp +++ b/moses/LM/Remote.cpp @@ -3,13 +3,13 @@ #include #include #include -#include -#include -#include -#include #include "Remote.h" #include "moses/Factor.h" +#if !defined(_WIN32) && !defined(_WIN64) +#include +#endif + namespace Moses { @@ -42,7 +42,11 @@ bool LanguageModelRemote::start(const std::string& host, int port) sock = socket(AF_INET, SOCK_STREAM, 0); hp = gethostbyname(host.c_str()); if (hp==NULL) { +#if defined(_WIN32) || defined(_WIN64) + fprintf(stderr, "gethostbyname failed\n"); +#else herror("gethostbyname failed"); +#endif exit(1); } diff --git a/moses/LM/Remote.h b/moses/LM/Remote.h index d50e3e9b4..b7a72d853 100644 --- a/moses/LM/Remote.h +++ b/moses/LM/Remote.h @@ -4,9 +4,15 @@ #include "SingleFactor.h" #include "moses/TypeDef.h" #include "moses/Factor.h" -#include #include + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include #include +#include +#endif namespace Moses { From e028eb78472a4e240f99f0a5db84e9de6fe37045 Mon Sep 17 00:00:00 2001 From: Ulrich Germann Date: Fri, 17 Apr 2015 23:12:32 +0100 Subject: [PATCH 29/53] A single output factor in Mmsapt can now be specified externally. (Before: hard-coded to 0.) --- moses/TranslationModel/UG/mmsapt.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/moses/TranslationModel/UG/mmsapt.cpp b/moses/TranslationModel/UG/mmsapt.cpp index c3dd8301d..124f972bd 100644 --- a/moses/TranslationModel/UG/mmsapt.cpp +++ b/moses/TranslationModel/UG/mmsapt.cpp @@ -66,7 +66,7 @@ namespace Moses Mmsapt:: Mmsapt(string const& line) : PhraseDictionary(line) - , ofactor(1,0) + // , ofactor(1,0) , m_tpc_ctr(0) { this->init(line); @@ -152,6 +152,10 @@ namespace Moses input_factor = atoi(param.insert(dflt).first->second.c_str()); // shouldn't that be a string? + pair dflt("output-factor","0"); + output_factor = atoi(param.insert(dflt).first->second.c_str()); + ofactor.assign(1,output_factor); + dflt = pair ("smooth",".01"); m_lbop_conf = atof(param.insert(dflt).first->second.c_str()); From 28d9e55379d54bd81c1b0c2246082a9a63b1b888 Mon Sep 17 00:00:00 2001 From: Ulrich Germann Date: Sat, 18 Apr 2015 16:53:57 +0100 Subject: [PATCH 30/53] Bug fix. --- moses/TranslationModel/UG/mmsapt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moses/TranslationModel/UG/mmsapt.cpp b/moses/TranslationModel/UG/mmsapt.cpp index 124f972bd..d24f571b3 100644 --- a/moses/TranslationModel/UG/mmsapt.cpp +++ b/moses/TranslationModel/UG/mmsapt.cpp @@ -152,7 +152,7 @@ namespace Moses input_factor = atoi(param.insert(dflt).first->second.c_str()); // shouldn't that be a string? - pair dflt("output-factor","0"); + dflt = pair ("output-factor","0"); output_factor = atoi(param.insert(dflt).first->second.c_str()); ofactor.assign(1,output_factor); From 637e8a17e8ce008dc63cfc262dcc128a32703e0f Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Sun, 19 Apr 2015 11:21:07 +0400 Subject: [PATCH 31/53] add pre tokenization cleaning script. In case training has bad, overlying long lines which blows up some taggers/segmenters, eg. mada --- scripts/ems/experiment.meta | 9 +- scripts/tokenizer/pre-tok-clean.perl | 46 +++++++++ .../training/wrappers/madamira-wrapper.perl | 93 +++++++++++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100755 scripts/tokenizer/pre-tok-clean.perl create mode 100755 scripts/training/wrappers/madamira-wrapper.perl diff --git a/scripts/ems/experiment.meta b/scripts/ems/experiment.meta index bc0a3b6b9..9a69fba36 100644 --- a/scripts/ems/experiment.meta +++ b/scripts/ems/experiment.meta @@ -7,8 +7,15 @@ get-corpus default-name: corpus/txt rerun-on-change: input-extension output-extension template: IN OUT $input-extension $output-extension +pre-tok-clean + in: raw-stem + out: pre-tok-cleaned + default-name: corpus/pre-tok-cleaned + pass-unless: pre-tok-clean + template: $pre-tok-clean IN $input-extension $output-extension OUT OUT.lines-retained + parallelizable: yes tokenize - in: raw-stem + in: pre-tok-cleaned out: tokenized-stem default-name: corpus/tok pass-unless: input-tokenizer output-tokenizer diff --git a/scripts/tokenizer/pre-tok-clean.perl b/scripts/tokenizer/pre-tok-clean.perl new file mode 100755 index 000000000..900e992ee --- /dev/null +++ b/scripts/tokenizer/pre-tok-clean.perl @@ -0,0 +1,46 @@ +#!/usr/bin/env perl + +use strict; + +my $minChars = $ARGV[0]; +my $maxChars = $ARGV[1]; +my $inputStem = $ARGV[2]; +my $source = $ARGV[3]; +my $target = $ARGV[4]; +my $outputStem = $ARGV[5]; +my $linesRetained = $ARGV[6]; + +open(IN_SOURCE, "<:encoding(UTF-8)", "$inputStem.$source") or die "cannot open $inputStem.$source"; +open(IN_TARGET, "<:encoding(UTF-8)", "$inputStem.$target") or die "cannot open $inputStem.$target"; + +open(OUT_SOURCE, ">:encoding(UTF-8)", "$outputStem.$source") or die "cannot open $outputStem.$source"; +open(OUT_TARGET, ">:encoding(UTF-8)", "$outputStem.$target") or die "cannot open $outputStem.$target"; + +open(LINE_RETAINED, ">:encoding(UTF-8)", "$linesRetained"); + +my $lineNum = 0; +while (my $lineSource = ) { + ++$lineNum; + #print STDERR "$lineNum "; + + chomp($lineSource); + my $lineTarget = ; + chomp($lineTarget); + + my $lenSource = length($lineSource); + my $lenTarget = length($lineTarget); + + if ($lenSource < $minChars || $lenSource > $maxChars + || $lenTarget < $minChars || $lenTarget > $maxChars) { + # do nothing + } + else { + print OUT_SOURCE "$lineSource\n"; + print OUT_TARGET "$lineTarget\n"; + print LINE_RETAINED "$lineNum\n"; + } +} + +close(OUT_SOURCE); +close(OUT_SOURCE); +close(LINE_RETAINED); diff --git a/scripts/training/wrappers/madamira-wrapper.perl b/scripts/training/wrappers/madamira-wrapper.perl new file mode 100755 index 000000000..6e7efe245 --- /dev/null +++ b/scripts/training/wrappers/madamira-wrapper.perl @@ -0,0 +1,93 @@ +#!/usr/bin/env perl + +use warnings; +use strict; +use File::Temp qw/tempfile/; +use Getopt::Long "GetOptions"; +use File::Basename; +use FindBin qw($RealBin); +use Cwd 'abs_path'; + +my $TMPDIR = "tmp"; +my $SCHEME = "D2"; +my $KEEP_TMP = 0; +my $MADA_DIR; + +GetOptions( + "scheme=s" => \$SCHEME, + "tmpdir=s" => \$TMPDIR, + "keep-tmp" => \$KEEP_TMP, + "mada-dir=s" => \$MADA_DIR + ) or die("ERROR: unknown options"); + +$TMPDIR = abs_path($TMPDIR); +print STDERR "TMPDIR=$TMPDIR \n"; + +#binmode(STDIN, ":utf8"); +#binmode(STDOUT, ":utf8"); + +$TMPDIR = "$TMPDIR/madamira.$$"; +`mkdir -p $TMPDIR`; +`mkdir -p $TMPDIR/split`; +`mkdir -p $TMPDIR/out`; + +my $infile = "$TMPDIR/input"; +print STDERR $infile."\n"; + +open(TMP,">$infile"); +while() { + print TMP $_; +} +close(TMP); + +my $cmd; + +# split input file +my $SPLIT_EXEC = `gsplit --help 2>/dev/null`; +if($SPLIT_EXEC) { + $SPLIT_EXEC = 'gsplit'; +} +else { + $SPLIT_EXEC = 'split'; +} + +$cmd = "$SPLIT_EXEC -l 10000 -a 7 -d $TMPDIR/input $TMPDIR/split/x"; +`$cmd`; + +$cmd = "cd $MADA_DIR && parallel --jobs 4 java -Xmx2500m -Xms2500m -XX:NewRatio=3 -jar $MADA_DIR/MADAMIRA.jar -rawinput {} -rawoutdir $TMPDIR/out -rawconfig $MADA_DIR/samples/sampleConfigFile.xml ::: $TMPDIR/split/x*"; +print STDERR "Executing: $cmd\n"; +`$cmd`; + +$cmd = "cat $TMPDIR/out/x*.mada > $infile.mada"; +print STDERR "Executing: $cmd\n"; +`$cmd`; + +# get stuff out of mada output +open(MADA_OUT,"<$infile.mada"); +#binmode(MADA_OUT, ":utf8"); +while(my $line = ) { + chop($line); + #print STDERR "line=$line \n"; + + if (index($line, "SENTENCE BREAK") == 0) { + # new sentence + #print STDERR "BREAK\n"; + print "\n"; + } + elsif (index($line, ";;WORD") == 0) { + # word + my $word = substr($line, 7, length($line) - 8); + #print STDERR "FOund $word\n"; + print "$word "; + } + else { + #print STDERR "NADA\n"; + } +} +close (MADA_OUT); + + +if ($KEEP_TMP == 0) { +# `rm -rf $TMPDIR`; +} + From 1b9dc6cfae48b5edc1eb138d650693b51dd40e97 Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Sun, 19 Apr 2015 11:50:50 +0400 Subject: [PATCH 32/53] more butinah tweaks --- scripts/ems/support/submit-grid.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ems/support/submit-grid.perl b/scripts/ems/support/submit-grid.perl index 087d41647..9997241e7 100755 --- a/scripts/ems/support/submit-grid.perl +++ b/scripts/ems/support/submit-grid.perl @@ -38,7 +38,7 @@ print $runFile "export PATH=\"$path\"\n\n"; print $runFile "export PERL5LIB=\"/share/apps/NYUAD/perl/gcc_4.9.1/5.20.1:/home/$user/perl5/lib/perl5\"\n\n"; print $runFile "module load NYUAD/2.0 \n"; -print $runFile "module load gcc/4.9.1 python/2.7.9 openmpi/1.8.3 boost cmake zlib jdk perl expat\n\n"; +print $runFile "module load gcc python/2.7.9 boost cmake zlib jdk perl expat \n\n"; my $emsDir = dirname($RealBin); From eb37437d09d66e15749a5a43bd5f99ea9b282700 Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Mon, 20 Apr 2015 16:18:51 +0400 Subject: [PATCH 33/53] don't output warnings. It wasn't originally there before the 'env perl' change. This script should be tightened up at some point, eg use strict, debug warning messages --- scripts/training/giza2bal.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/training/giza2bal.pl b/scripts/training/giza2bal.pl index 8daa8e916..56fc9a466 100755 --- a/scripts/training/giza2bal.pl +++ b/scripts/training/giza2bal.pl @@ -7,7 +7,7 @@ #Copyright Marcello Federico, November 2004 -use warnings; +#use warnings; ($cnt,$dir,$inv)=(); @@ -19,7 +19,7 @@ while ($w=shift @ARGV){ my $lc = 0; -if (!$dir || !inv){ +if (!$dir || !$inv){ print "usage: giza2bal.pl [-c ] -d -i \n"; print "input files can be also commands, e.g. -d \"gunzip -c file.gz\"\n"; exit(0); From 95435f2a2ee3a66fe3048c0d2bce42e4380a73a4 Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Mon, 20 Apr 2015 20:40:50 +0400 Subject: [PATCH 34/53] better detection of pigz --- scripts/training/train-model.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/training/train-model.perl b/scripts/training/train-model.perl index 6f850d609..fb63d4bbd 100755 --- a/scripts/training/train-model.perl +++ b/scripts/training/train-model.perl @@ -405,8 +405,8 @@ else { $SORT_EXEC = 'sort'; } -my $GZIP_EXEC; # = which("pigz"); -if(-f "/usr/bin/pigz") { +my $GZIP_EXEC; +if(`which pigz`) { $GZIP_EXEC = 'pigz'; } else { From 5a3d5b6bdd8e72d730741b189636c5b2132ef1e0 Mon Sep 17 00:00:00 2001 From: Rico Sennrich Date: Thu, 16 Apr 2015 14:16:59 +0100 Subject: [PATCH 35/53] EMS: LM:mock-parse can be actual parser --- scripts/ems/experiment.meta | 26 ++++++++++++++++---------- scripts/training/strip-xml.perl | 17 +++++++++++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) create mode 100755 scripts/training/strip-xml.perl diff --git a/scripts/ems/experiment.meta b/scripts/ems/experiment.meta index 9a69fba36..ead9ebe03 100644 --- a/scripts/ems/experiment.meta +++ b/scripts/ems/experiment.meta @@ -211,8 +211,14 @@ split default-name: lm/split pass-unless: output-splitter template: $output-splitter -model IN1.$output-extension < IN > OUT +strip + in: split-corpus + out: stripped-corpus + default-name: lm/stripped + pass-unless: mock-output-parser-lm + template: $moses-script-dir/training/strip-xml.perl < IN > OUT train - in: split-corpus + in: stripped-corpus out: lm default-name: lm/lm ignore-if: rlm-training @@ -227,7 +233,7 @@ randomize pass-unless: lm-randomizer ignore-if: rlm-training train-randomized - in: split-corpus + in: stripped-corpus out: rlm default-name: lm/rlm ignore-unless: rlm-training @@ -960,21 +966,21 @@ split-reference-devtest ignore-unless: use-mira multiref: $moses-script-dir/ems/support/run-command-on-multiple-refsets.perl template: $output-splitter -model IN1.$output-extension < IN > OUT -reduce-reference +strip-reference in: split-ref out: reference - default-name: tuning/reference.reduced + default-name: tuning/reference.stripped pass-unless: mock-output-parser-references multiref: $moses-script-dir/ems/support/run-command-on-multiple-refsets.perl - template: $moses-script-dir/training/reduce-factors.perl --factor 0 --xml 1 --corpus IN --reduced-corpus OUT && $moses-script-dir/training/wrappers/mosesxml2brackets.py < IN > OUT.trees -reduce-reference-devtest + template: $moses-script-dir/training/strip-xml.perl < IN > OUT && $moses-script-dir/training/wrappers/mosesxml2brackets.py < IN > OUT.trees +strip-reference-devtest in: split-ref-devtest out: reference - default-name: tuning/reference.devtest.reduced + default-name: tuning/reference.devtest.stripped pass-unless: mock-output-parser-references ignore-unless: use-mira multiref: $moses-script-dir/ems/support/run-command-on-multiple-refsets.perl - template: $moses-script-dir/training/reduce-factors.perl --factor 0 --xml 1 --corpus IN --reduced-corpus OUT && $moses-script-dir/training/wrappers/mosesxml2brackets.py < IN > OUT.trees + template: $moses-script-dir/training/strip-xml.perl < IN > OUT && $moses-script-dir/training/wrappers/mosesxml2brackets.py < IN > OUT.trees filter in: input TRAINING:sigtest-filter-phrase-translation-table TRAINING:sigtest-filter-reordering-table TRAINING:corpus-mml-prefilter=OR=TRAINING:corpus-mml-postfilter=OR=TRAINING:domains TRAINING:transliteration-table out: filtered-dir @@ -1231,13 +1237,13 @@ lowercase-reference pass-if: recaser multiref: $moses-script-dir/ems/support/run-command-on-multiple-refsets.perl template: $output-lowercaser < IN > OUT -reduce-reference +strip-reference in: lowercased-reference out: reference default-name: evaluation/reference pass-unless: mock-output-parser-references multiref: $moses-script-dir/ems/support/run-command-on-multiple-refsets.perl - template: $moses-script-dir/training/reduce-factors.perl --factor 0 --xml 1 --corpus IN --reduced-corpus OUT && $moses-script-dir/training/wrappers/mosesxml2brackets.py < IN > OUT.trees + template: $moses-script-dir/training/strip-xml.perl < IN > OUT && $moses-script-dir/training/wrappers/mosesxml2brackets.py < IN > OUT.trees wade in: filtered-dir truecased-input tokenized-reference alignment system-output out: wade-analysis diff --git a/scripts/training/strip-xml.perl b/scripts/training/strip-xml.perl new file mode 100755 index 000000000..0f403d15d --- /dev/null +++ b/scripts/training/strip-xml.perl @@ -0,0 +1,17 @@ +#!/usr/bin/env perl + +# strip text file of any XML markup + +binmode(STDIN, ":utf8"); +binmode(STDOUT, ":utf8"); + +use strict; + +while() { + s/<\S[^>]*>/ /g; + chomp; + s/ +/ /g; + s/^ //; + print $_; + print "\n"; +} From 15d3c3f25977d81b1ba56185e508410ac72a0c42 Mon Sep 17 00:00:00 2001 From: Rico Sennrich Date: Tue, 21 Apr 2015 14:04:25 +0100 Subject: [PATCH 36/53] be more tolerant about xml input --- scripts/ems/support/prepare-fast-align.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ems/support/prepare-fast-align.perl b/scripts/ems/support/prepare-fast-align.perl index 4ecc14cd7..54c124af0 100755 --- a/scripts/ems/support/prepare-fast-align.perl +++ b/scripts/ems/support/prepare-fast-align.perl @@ -23,7 +23,7 @@ while(my $source = ) { # remove markup foreach my $line (\$source,\$target) { - $$line =~ s/\<[^\>]+\>//g; + $$line =~ s/\<[^\>]+\>/ /g; $$line =~ s/\s+/ /g; $$line =~ s/^ //; $$line =~ s/ $//; From ab01d30687da1f1091ccfbf90fb1c7bc0eaf5f1b Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Tue, 21 Apr 2015 17:53:46 +0400 Subject: [PATCH 37/53] make sure GetOptions doesn consume -T by confusing it with --text --- scripts/ems/support/lmplz-wrapper.perl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/ems/support/lmplz-wrapper.perl b/scripts/ems/support/lmplz-wrapper.perl index f79399d6b..eadca6263 100755 --- a/scripts/ems/support/lmplz-wrapper.perl +++ b/scripts/ems/support/lmplz-wrapper.perl @@ -4,8 +4,10 @@ use warnings; use strict; use Getopt::Long "GetOptions"; +Getopt::Long::config("no_auto_abbrev"); Getopt::Long::config("pass_through"); + my ($TEXT,$ORDER,$BIN,$LM); &GetOptions('text=s' => \$TEXT, @@ -16,8 +18,9 @@ my ($TEXT,$ORDER,$BIN,$LM); die("ERROR: specify at least --bin BIN --text CORPUS --lm LM and --order N!") unless defined($BIN) && defined($TEXT) && defined($LM) && defined($ORDER); -my $cmd = "$BIN --text $TEXT --order $ORDER --arpa $LM"; -$cmd .= " " . join(' ', @ARGV) if scalar(@ARGV); # Pass remaining args through. +my $settings = join(' ', @ARGV); +#print STDERR "settngs=$settings \n"; +my $cmd = "$BIN --text $TEXT --order $ORDER --arpa $LM $settings"; print "exec: $cmd\n"; `$cmd`; From c15f3ef068d07a7bc5d491f72e319e13c9620acf Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Tue, 21 Apr 2015 17:54:34 +0400 Subject: [PATCH 38/53] duplicated functionality with ems/support/lmplz-wrapper.perl --- scripts/generic/trainlm-lmplz.perl | 41 ------------------------------ 1 file changed, 41 deletions(-) delete mode 100755 scripts/generic/trainlm-lmplz.perl diff --git a/scripts/generic/trainlm-lmplz.perl b/scripts/generic/trainlm-lmplz.perl deleted file mode 100755 index c7e9e4553..000000000 --- a/scripts/generic/trainlm-lmplz.perl +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env perl - -# Compatible with sri LM-creating script, eg. -# ngram-count -order 5 -interpolate -wbdiscount -unk -text corpus.txt -lm lm.txt -# To use it in the EMS, add this to the [LM] section -# lm-training = "$moses-script-dir/generic/trainlm-lmplz.perl -lmplz $lmplz" -# settings = "-T $working-dir/tmp -S 10G" -# Also, make sure that $lmplz is defined (in the [LM] or [GENERAL] section. -# It should point to the binary file -# lmplz = /home/waziz/workspace/github/moses/bin/lmplz - -use warnings; -use strict; -use FindBin qw($RealBin); -use Getopt::Long qw/GetOptionsFromArray/; -#use Getopt::Long; -Getopt::Long::Configure("pass_through", "no_ignore_case"); - -my $order = 3; # order of language model (default trigram) -my $corpus; # input text data -my $lm; # generated language model -my $lmplz; # bin directory of IRSTLM -my $help = 0; - -my @optconfig = ( - "-order=s" => \$order, - "-text=s" => \$corpus, - "-lm=s" => \$lm, - "-lmplz=s" => \$lmplz, -); - -GetOptionsFromArray(\@ARGV, @optconfig); -die("ERROR: please set text") unless defined($corpus); -die("ERROR: please set lm") unless defined($lm); -die("ERROR: please set lmplz") unless defined($lmplz); - -my $settings = join(' ', @ARGV); -my $cmd = "$lmplz --order $order $settings < $corpus > $lm"; - -print STDERR "EXECUTING $cmd\n"; -`$cmd`; From b2d821a14176d37467375e671ebe9fc83260e9a8 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Wed, 22 Apr 2015 09:59:05 +0700 Subject: [PATCH 39/53] Unify tokenize() into util, and unit-test it. The duplicate definition works fine in environments where the inline definition becomes a weak symbol in the object file, but if it gets generated as a regular definition, the duplicate definition causes link problems. In most call sites the return value could easily be made const, which gives both the reader and the compiler a bit more certainty about the code's intentions. In theory this may help performance, but it's mainly for clarity. The comments are based on reverse-engineering, and the unit tests are based on the comments. It's possible that some of what's in there is not essential, in which case, don't feel bad about changing it! I left a third identical definition in place, though I updated it with my changes to avoid creeping divergence, and noted the duplication in a comment. It would be nice to get rid of this definition as well, but it'd introduce headers from the main Moses tree into biconcor, which may be against policy. --- biconcor/phrase-lookup.cpp | 11 +++++--- .../PhraseDictionaryMultiModelCounts.cpp | 26 ++----------------- phrase-extract/DomainFeature.cpp | 5 ++-- phrase-extract/DomainFeature.h | 2 -- phrase-extract/SentenceAlignment.cpp | 7 ++--- .../SentenceAlignmentWithSyntax.cpp | 8 +++--- phrase-extract/consolidate-direct-main.cpp | 5 ++-- phrase-extract/consolidate-reverse-main.cpp | 10 +++---- phrase-extract/extract-ghkm/XmlTreeParser.cpp | 3 ++- phrase-extract/pcfg-common/xml_tree_parser.cc | 3 ++- phrase-extract/relax-parse-main.cpp | 5 ++-- phrase-extract/relax-parse.h | 2 +- phrase-extract/statistics-main.cpp | 5 ++-- .../syntax-common/xml_tree_parser.cc | 3 ++- phrase-extract/tables-core.cpp | 26 ++----------------- phrase-extract/tables-core.h | 2 -- 16 files changed, 41 insertions(+), 82 deletions(-) diff --git a/biconcor/phrase-lookup.cpp b/biconcor/phrase-lookup.cpp index 3ef82e73a..60ab8db66 100644 --- a/biconcor/phrase-lookup.cpp +++ b/biconcor/phrase-lookup.cpp @@ -109,14 +109,17 @@ size_t lookup( string query ) return suffixArray.Count( queryString ); } -vector tokenize( const char input[] ) +// Duplicate of definition in util/tokenize.hh. +// TODO: Can we de-duplicate this? At the time of writing biconcor does not +// use util at all. +vector tokenize(const char input[]) { vector< string > token; bool betweenWords = true; int start=0; - int i=0; - for(; input[i] != '\0'; i++) { - bool isSpace = (input[i] == ' ' || input[i] == '\t'); + int i; + for(i = 0; input[i] != '\0'; i++) { + const bool isSpace = (input[i] == ' ' || input[i] == '\t'); if (!isSpace && betweenWords) { start = i; diff --git a/moses/TranslationModel/PhraseDictionaryMultiModelCounts.cpp b/moses/TranslationModel/PhraseDictionaryMultiModelCounts.cpp index c632f9ff2..444557f9b 100644 --- a/moses/TranslationModel/PhraseDictionaryMultiModelCounts.cpp +++ b/moses/TranslationModel/PhraseDictionaryMultiModelCounts.cpp @@ -17,6 +17,7 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ***********************************************************************/ #include "util/exception.hh" +#include "util/tokenize.hh" #include "moses/TranslationModel/PhraseDictionaryMultiModelCounts.h" using namespace std; @@ -30,29 +31,6 @@ void OutputVec(const vector &vec) cerr << endl; } -// from phrase-extract/tables-core.cpp -inline vector tokenize( const char* input ) -{ - vector< string > token; - bool betweenWords = true; - int start=0; - int i=0; - for(; input[i] != '\0'; i++) { - bool isSpace = (input[i] == ' ' || input[i] == '\t'); - - if (!isSpace && betweenWords) { - start = i; - betweenWords = false; - } else if (isSpace && !betweenWords) { - token.push_back( string( input+start, i-start ) ); - betweenWords = true; - } - } - if (!betweenWords) - token.push_back( string( input+start, i-start ) ); - return token; -} - namespace Moses { @@ -464,7 +442,7 @@ void PhraseDictionaryMultiModelCounts::LoadLexicalTable( string &fileName, lexic i++; if (i%100000 == 0) cerr << "." << flush; - vector token = tokenize( line.c_str() ); + const vector token = util::tokenize( line.c_str() ); if (token.size() != 4) { cerr << "line " << i << " in " << fileName << " has wrong number of tokens, skipping:\n" diff --git a/phrase-extract/DomainFeature.cpp b/phrase-extract/DomainFeature.cpp index 899eb9f1c..0d2b96a8a 100644 --- a/phrase-extract/DomainFeature.cpp +++ b/phrase-extract/DomainFeature.cpp @@ -2,6 +2,7 @@ #include "ExtractionPhrasePair.h" #include "tables-core.h" #include "InputFileStream.h" +#include "util/tokenize.hh" using namespace std; @@ -17,7 +18,7 @@ void Domain::load( const std::string &domainFileName ) string line; while(getline(*fileP, line)) { // read - vector< string > domainSpecLine = tokenize( line.c_str() ); + const vector< string > domainSpecLine = util::tokenize( line.c_str() ); int lineNumber; if (domainSpecLine.size() != 2 || ! sscanf(domainSpecLine[0].c_str(), "%d", &lineNumber)) { @@ -25,7 +26,7 @@ void Domain::load( const std::string &domainFileName ) exit(1); } // store - string &name = domainSpecLine[1]; + const string &name = domainSpecLine[1]; spec.push_back( make_pair( lineNumber, name )); if (name2id.find( name ) == name2id.end()) { name2id[ name ] = list.size(); diff --git a/phrase-extract/DomainFeature.h b/phrase-extract/DomainFeature.h index 040a5fc72..95babb6c2 100644 --- a/phrase-extract/DomainFeature.h +++ b/phrase-extract/DomainFeature.h @@ -14,8 +14,6 @@ #include "ScoreFeature.h" -extern std::vector tokenize( const char*); - namespace MosesTraining { diff --git a/phrase-extract/SentenceAlignment.cpp b/phrase-extract/SentenceAlignment.cpp index ee7f27ed9..21c1a1dbd 100644 --- a/phrase-extract/SentenceAlignment.cpp +++ b/phrase-extract/SentenceAlignment.cpp @@ -24,6 +24,7 @@ #include #include "tables-core.h" +#include "util/tokenize.hh" using namespace std; @@ -40,7 +41,7 @@ void addBoundaryWords(vector &phrase) bool SentenceAlignment::processTargetSentence(const char * targetString, int, bool boundaryRules) { - target = tokenize(targetString); + target = util::tokenize(targetString); if (boundaryRules) addBoundaryWords(target); return true; @@ -48,7 +49,7 @@ bool SentenceAlignment::processTargetSentence(const char * targetString, int, bo bool SentenceAlignment::processSourceSentence(const char * sourceString, int, bool boundaryRules) { - source = tokenize(sourceString); + source = util::tokenize(sourceString); if (boundaryRules) addBoundaryWords(source); return true; @@ -89,7 +90,7 @@ bool SentenceAlignment::create(const char targetString[], } // reading in alignments - vector alignmentSequence = tokenize( alignmentString ); + vector alignmentSequence = util::tokenize( alignmentString ); for(size_t i=0; i #include "InputFileStream.h" #include "OutputFileStream.h" +#include "util/tokenize.hh" using namespace std; -std::vector tokenize( const char [] ); - vector< string > splitLine(const char *line) { vector< string > item; @@ -109,7 +108,7 @@ int main(int argc, char* argv[]) if (! getLine(fileDirectP, itemDirect )) break; - vector< string > count = tokenize( itemDirect[4].c_str() ); + const vector< string > count = util::tokenize( itemDirect[4].c_str() ); float countEF = atof(count[0].c_str()); float countF = atof(count[1].c_str()); float prob = countF/countEF; diff --git a/phrase-extract/consolidate-reverse-main.cpp b/phrase-extract/consolidate-reverse-main.cpp index e2b0ad473..abba063a3 100644 --- a/phrase-extract/consolidate-reverse-main.cpp +++ b/phrase-extract/consolidate-reverse-main.cpp @@ -28,6 +28,7 @@ #include "tables-core.h" #include "InputFileStream.h" +#include "util/tokenize.hh" using namespace std; @@ -165,8 +166,8 @@ void processFiles( char* fileNameDirect, char* fileNameIndirect, char* fileNameC fileConsolidated << " ||| " << reverseAlignment(itemDirect[3]); // counts, for debugging - vector directCounts = tokenize(itemDirect[4].c_str()); - vector indirectCounts = tokenize(itemIndirect[4].c_str()); + const vector directCounts = util::tokenize(itemDirect[4].c_str()); + const vector indirectCounts = util::tokenize(itemIndirect[4].c_str()); fileConsolidated << "||| " << directCounts[0] << " " << indirectCounts[0]; // output rule count if present in either file if (indirectCounts.size() > 1) { @@ -199,7 +200,6 @@ bool getLine( istream &fileP, vector< string > &item ) vector< string > splitLine(const char *line) { vector< string > item; - bool betweenWords = true; int start=0; int i=0; for(; line[i] != '\0'; i++) { @@ -223,10 +223,10 @@ string reverseAlignment(const string &alignments) { stringstream ret(""); - vector alignToks = tokenize(alignments.c_str()); + const vector alignToks = util::tokenize(alignments.c_str()); for (size_t i = 0; i < alignToks.size(); ++i) { - string &alignPair = alignToks[i]; + const string &alignPair = alignToks[i]; vector alignPoints; Tokenize(alignPoints, alignPair, "-"); assert(alignPoints.size() == 2); diff --git a/phrase-extract/extract-ghkm/XmlTreeParser.cpp b/phrase-extract/extract-ghkm/XmlTreeParser.cpp index 2f28c3244..267906b4c 100644 --- a/phrase-extract/extract-ghkm/XmlTreeParser.cpp +++ b/phrase-extract/extract-ghkm/XmlTreeParser.cpp @@ -23,6 +23,7 @@ #include "tables-core.h" #include "XmlException.h" #include "XmlTree.h" +#include "util/tokenize.hh" #include #include @@ -56,7 +57,7 @@ std::auto_ptr XmlTreeParser::Parse(const std::string &line) m_tree.ConnectNodes(); SyntaxNode *root = m_tree.GetTop(); assert(root); - m_words = tokenize(m_line.c_str()); + m_words = util::tokenize(m_line.c_str()); return ConvertTree(*root, m_words); } diff --git a/phrase-extract/pcfg-common/xml_tree_parser.cc b/phrase-extract/pcfg-common/xml_tree_parser.cc index 3d9291994..6a2f3fc51 100644 --- a/phrase-extract/pcfg-common/xml_tree_parser.cc +++ b/phrase-extract/pcfg-common/xml_tree_parser.cc @@ -25,6 +25,7 @@ #include "tables-core.h" #include "XmlException.h" #include "XmlTree.h" +#include "util/tokenize.hh" #include "syntax-common/exception.h" @@ -51,7 +52,7 @@ std::auto_ptr XmlTreeParser::Parse(const std::string &line) { // There is no XML tree. return std::auto_ptr(); } - m_words = tokenize(m_line.c_str()); + m_words = util::tokenize(m_line.c_str()); return ConvertTree(*root, m_words); } diff --git a/phrase-extract/relax-parse-main.cpp b/phrase-extract/relax-parse-main.cpp index a6d50cef5..ec7bd25fd 100644 --- a/phrase-extract/relax-parse-main.cpp +++ b/phrase-extract/relax-parse-main.cpp @@ -21,6 +21,7 @@ #include "relax-parse.h" #include "tables-core.h" +#include "util/tokenize.hh" using namespace std; using namespace MosesTraining; @@ -44,7 +45,7 @@ int main(int argc, char* argv[]) map< string, int > topLabelCollection; // count of top labels, not used SyntaxTree tree; ProcessAndStripXMLTags( inBufferString, tree, labelCollection, topLabelCollection, false ); - vector< string > inWords = tokenize( inBufferString.c_str() ); + const vector< string > inWords = util::tokenize( inBufferString.c_str() ); // output tree // cerr << "BEFORE:" << endl << tree; @@ -104,7 +105,7 @@ void init(int argc, char* argv[]) } } -void store( SyntaxTree &tree, vector< string > &words ) +void store( SyntaxTree &tree, const vector< string > &words ) { // output words for( size_t i=0; i &words ); +void store( MosesTraining::SyntaxTree &tree, const std::vector &words ); void LeftBinarize( MosesTraining::SyntaxTree &tree, MosesTraining::ParentNodes &parents ); void RightBinarize( MosesTraining::SyntaxTree &tree, MosesTraining::ParentNodes &parents ); void SAMT( MosesTraining::SyntaxTree &tree, MosesTraining::ParentNodes &parents ); diff --git a/phrase-extract/statistics-main.cpp b/phrase-extract/statistics-main.cpp index a6c0b74db..d797b9f45 100644 --- a/phrase-extract/statistics-main.cpp +++ b/phrase-extract/statistics-main.cpp @@ -14,6 +14,7 @@ #include "AlignmentPhrase.h" #include "tables-core.h" #include "InputFileStream.h" +#include "util/tokenize.hh" using namespace std; using namespace MosesTraining; @@ -237,7 +238,7 @@ void processPhrasePairs( vector< PhraseAlignment > &phrasePair ) bool PhraseAlignment::create(const char line[], int lineID ) { - vector< string > token = tokenize( line ); + const vector< string > token = util::tokenize( line ); int item = 1; PHRASE phraseF, phraseE; for (size_t j=0; j token = tokenize( line.c_str() ); + const vector token = util::tokenize( line.c_str() ); if (token.size() != 3) { cerr << "line " << i << " in " << filePath << " has wrong number of tokens, skipping:\n" << token.size() << " " << token[0] << " " << line << endl; diff --git a/phrase-extract/syntax-common/xml_tree_parser.cc b/phrase-extract/syntax-common/xml_tree_parser.cc index c4363a3e2..47fda1c2d 100644 --- a/phrase-extract/syntax-common/xml_tree_parser.cc +++ b/phrase-extract/syntax-common/xml_tree_parser.cc @@ -3,6 +3,7 @@ #include "tables-core.h" #include "XmlException.h" #include "XmlTree.h" +#include "util/tokenize.hh" #include #include @@ -24,7 +25,7 @@ StringTree *XmlTreeParser::Parse(const std::string &line) { tree_.ConnectNodes(); SyntaxNode *root = tree_.GetTop(); assert(root); - words_ = tokenize(line_.c_str()); + words_ = util::tokenize(line_.c_str()); return ConvertTree(*root, words_); } diff --git a/phrase-extract/tables-core.cpp b/phrase-extract/tables-core.cpp index 30c1544e9..93c5041dd 100644 --- a/phrase-extract/tables-core.cpp +++ b/phrase-extract/tables-core.cpp @@ -1,5 +1,6 @@ // $Id$ //#include "beammain.h" +#include "util/tokenize.hh" #include "tables-core.h" #define TABLE_LINE_MAX_LENGTH 1000 @@ -7,29 +8,6 @@ using namespace std; -// as in beamdecoder/tables.cpp -vector tokenize( const char* input ) -{ - vector< string > token; - bool betweenWords = true; - int start=0; - int i=0; - for(; input[i] != '\0'; i++) { - bool isSpace = (input[i] == ' ' || input[i] == '\t'); - - if (!isSpace && betweenWords) { - start = i; - betweenWords = false; - } else if (isSpace && !betweenWords) { - token.push_back( string( input+start, i-start ) ); - betweenWords = true; - } - } - if (!betweenWords) - token.push_back( string( input+start, i-start ) ); - return token; -} - namespace MosesTraining { @@ -107,7 +85,7 @@ void DTable::load( const string& fileName ) abort(); } - vector token = tokenize(line.c_str()); + const vector token = util::tokenize(line.c_str()); if (token.size() < 2) { cerr << "line " << i << " in " << fileName << " too short, skipping\n"; continue; diff --git a/phrase-extract/tables-core.h b/phrase-extract/tables-core.h index 44545d3a0..011fe09e6 100644 --- a/phrase-extract/tables-core.h +++ b/phrase-extract/tables-core.h @@ -12,8 +12,6 @@ #include #include -extern std::vector tokenize( const char*); - namespace MosesTraining { From 10a0a7b05a190c10af9a926160fed06915691af5 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Wed, 22 Apr 2015 10:18:02 +0700 Subject: [PATCH 40/53] Add new files. Oops. Forgot these in my previous commit. Sorry! --- util/tokenize.hh | 42 ++++++++++++++++++++++++++ util/tokenize_test.cc | 69 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 util/tokenize.hh create mode 100644 util/tokenize_test.cc diff --git a/util/tokenize.hh b/util/tokenize.hh new file mode 100644 index 000000000..f4f3289bc --- /dev/null +++ b/util/tokenize.hh @@ -0,0 +1,42 @@ +#ifndef TOKENIZE_H +#define TOKENIZE_H + +#include +#include + +namespace util +{ + +/** Split input text into a series of tokens. + * + * Splits on spaces and tabs, no other whitespace characters, and is not + * locale-sensitive. + * + * The spaces themselves are not included. A sequence of consecutive space/tab + * characters count as one. + */ +inline std::vector tokenize(const char input[]) +{ + std::vector token; + bool betweenWords = true; + int start = 0; + int i; + for(i = 0; input[i] != '\0'; i++) { + const bool isSpace = (input[i] == ' ' || input[i] == '\t'); + + if (!isSpace && betweenWords) { + start = i; + betweenWords = false; + } else if (isSpace && !betweenWords) { + token.push_back( std::string( input+start, i-start ) ); + betweenWords = true; + } + } + if (!betweenWords) + token.push_back( std::string( input+start, i-start ) ); + return token; +} + +} // namespace util + +#endif diff --git a/util/tokenize_test.cc b/util/tokenize_test.cc new file mode 100644 index 000000000..d879fa97f --- /dev/null +++ b/util/tokenize_test.cc @@ -0,0 +1,69 @@ +#include "util/tokenize.hh" + +#define BOOST_TEST_MODULE TokenizeTest +#include + +namespace util +{ +namespace +{ + +BOOST_AUTO_TEST_CASE(empty_text_yields_empty_vector) +{ + const std::vector tokens = util::tokenize(""); + BOOST_CHECK_EQUAL(tokens.size(), 0); +} + +BOOST_AUTO_TEST_CASE(whitespace_only_yields_empty_vector) +{ + const std::vector tokens = util::tokenize(" "); + BOOST_CHECK_EQUAL(tokens.size(), 0); +} + +BOOST_AUTO_TEST_CASE(parses_single_token) +{ + const std::vector tokens = util::tokenize("mytoken"); + BOOST_CHECK_EQUAL(tokens.size(), 1); + BOOST_CHECK_EQUAL(tokens[0], "mytoken"); +} + +BOOST_AUTO_TEST_CASE(ignores_leading_whitespace) +{ + const std::vector tokens = util::tokenize(" \t mytoken"); + BOOST_CHECK_EQUAL(tokens.size(), 1); + BOOST_CHECK_EQUAL(tokens[0], "mytoken"); +} + +BOOST_AUTO_TEST_CASE(ignores_trailing_whitespace) +{ + const std::vector tokens = util::tokenize("mytoken \t "); + BOOST_CHECK_EQUAL(tokens.size(), 1); + BOOST_CHECK_EQUAL(tokens[0], "mytoken"); +} + +BOOST_AUTO_TEST_CASE(splits_tokens_on_tabs) +{ + const std::vector tokens = util::tokenize("one\ttwo"); + BOOST_CHECK_EQUAL(tokens.size(), 2); + BOOST_CHECK_EQUAL(tokens[0], "one"); + BOOST_CHECK_EQUAL(tokens[1], "two"); +} + +BOOST_AUTO_TEST_CASE(splits_tokens_on_spaces) +{ + const std::vector tokens = util::tokenize("one two"); + BOOST_CHECK_EQUAL(tokens.size(), 2); + BOOST_CHECK_EQUAL(tokens[0], "one"); + BOOST_CHECK_EQUAL(tokens[1], "two"); +} + +BOOST_AUTO_TEST_CASE(treats_sequence_of_space_as_one_space) +{ + const std::vector tokens = util::tokenize("one\t \ttwo"); + BOOST_CHECK_EQUAL(tokens.size(), 2); + BOOST_CHECK_EQUAL(tokens[0], "one"); + BOOST_CHECK_EQUAL(tokens[1], "two"); +} + +} // namespace +} // namespace util From 32722ab5b1477cb55bce2ba5d0c2620446cff8a9 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Wed, 22 Apr 2015 10:35:18 +0700 Subject: [PATCH 41/53] Support tokenize(const std::string &) as well. Convenience wrapper: the actual function takes a const char[], but many of the call sites want to pass a string and have to call its c_str() first. --- .../PhraseDictionaryMultiModelCounts.cpp | 2 +- phrase-extract/DomainFeature.cpp | 2 +- phrase-extract/SentenceAlignmentWithSyntax.cpp | 4 ++-- phrase-extract/consolidate-direct-main.cpp | 2 +- phrase-extract/consolidate-reverse-main.cpp | 6 +++--- phrase-extract/extract-ghkm/XmlTreeParser.cpp | 2 +- phrase-extract/pcfg-common/xml_tree_parser.cc | 2 +- phrase-extract/relax-parse-main.cpp | 2 +- phrase-extract/statistics-main.cpp | 2 +- phrase-extract/syntax-common/xml_tree_parser.cc | 2 +- phrase-extract/tables-core.cpp | 2 +- util/tokenize.hh | 9 +++++++++ 12 files changed, 23 insertions(+), 14 deletions(-) diff --git a/moses/TranslationModel/PhraseDictionaryMultiModelCounts.cpp b/moses/TranslationModel/PhraseDictionaryMultiModelCounts.cpp index 444557f9b..773e027cc 100644 --- a/moses/TranslationModel/PhraseDictionaryMultiModelCounts.cpp +++ b/moses/TranslationModel/PhraseDictionaryMultiModelCounts.cpp @@ -442,7 +442,7 @@ void PhraseDictionaryMultiModelCounts::LoadLexicalTable( string &fileName, lexic i++; if (i%100000 == 0) cerr << "." << flush; - const vector token = util::tokenize( line.c_str() ); + const vector token = util::tokenize( line ); if (token.size() != 4) { cerr << "line " << i << " in " << fileName << " has wrong number of tokens, skipping:\n" diff --git a/phrase-extract/DomainFeature.cpp b/phrase-extract/DomainFeature.cpp index 0d2b96a8a..d5138ba9b 100644 --- a/phrase-extract/DomainFeature.cpp +++ b/phrase-extract/DomainFeature.cpp @@ -18,7 +18,7 @@ void Domain::load( const std::string &domainFileName ) string line; while(getline(*fileP, line)) { // read - const vector< string > domainSpecLine = util::tokenize( line.c_str() ); + const vector< string > domainSpecLine = util::tokenize( line ); int lineNumber; if (domainSpecLine.size() != 2 || ! sscanf(domainSpecLine[0].c_str(), "%d", &lineNumber)) { diff --git a/phrase-extract/SentenceAlignmentWithSyntax.cpp b/phrase-extract/SentenceAlignmentWithSyntax.cpp index 7403243ab..4fd2355ae 100644 --- a/phrase-extract/SentenceAlignmentWithSyntax.cpp +++ b/phrase-extract/SentenceAlignmentWithSyntax.cpp @@ -50,7 +50,7 @@ bool SentenceAlignmentWithSyntax::processTargetSentence(const char * targetStrin << sentenceID << ": " << e.getMsg() << std::endl; return false; } - target = util::tokenize(targetStringCPP.c_str()); + target = util::tokenize(targetStringCPP); return true; } @@ -71,7 +71,7 @@ bool SentenceAlignmentWithSyntax::processSourceSentence(const char * sourceStrin << sentenceID << ": " << e.getMsg() << std::endl; return false; } - source = util::tokenize(sourceStringCPP.c_str()); + source = util::tokenize(sourceStringCPP); return true; } diff --git a/phrase-extract/consolidate-direct-main.cpp b/phrase-extract/consolidate-direct-main.cpp index e85a897ef..d25197372 100644 --- a/phrase-extract/consolidate-direct-main.cpp +++ b/phrase-extract/consolidate-direct-main.cpp @@ -108,7 +108,7 @@ int main(int argc, char* argv[]) if (! getLine(fileDirectP, itemDirect )) break; - const vector< string > count = util::tokenize( itemDirect[4].c_str() ); + const vector< string > count = util::tokenize( itemDirect[4] ); float countEF = atof(count[0].c_str()); float countF = atof(count[1].c_str()); float prob = countF/countEF; diff --git a/phrase-extract/consolidate-reverse-main.cpp b/phrase-extract/consolidate-reverse-main.cpp index abba063a3..bce496a0c 100644 --- a/phrase-extract/consolidate-reverse-main.cpp +++ b/phrase-extract/consolidate-reverse-main.cpp @@ -166,8 +166,8 @@ void processFiles( char* fileNameDirect, char* fileNameIndirect, char* fileNameC fileConsolidated << " ||| " << reverseAlignment(itemDirect[3]); // counts, for debugging - const vector directCounts = util::tokenize(itemDirect[4].c_str()); - const vector indirectCounts = util::tokenize(itemIndirect[4].c_str()); + const vector directCounts = util::tokenize(itemDirect[4]); + const vector indirectCounts = util::tokenize(itemIndirect[4]); fileConsolidated << "||| " << directCounts[0] << " " << indirectCounts[0]; // output rule count if present in either file if (indirectCounts.size() > 1) { @@ -223,7 +223,7 @@ string reverseAlignment(const string &alignments) { stringstream ret(""); - const vector alignToks = util::tokenize(alignments.c_str()); + const vector alignToks = util::tokenize(alignments); for (size_t i = 0; i < alignToks.size(); ++i) { const string &alignPair = alignToks[i]; diff --git a/phrase-extract/extract-ghkm/XmlTreeParser.cpp b/phrase-extract/extract-ghkm/XmlTreeParser.cpp index 267906b4c..f9800c8e0 100644 --- a/phrase-extract/extract-ghkm/XmlTreeParser.cpp +++ b/phrase-extract/extract-ghkm/XmlTreeParser.cpp @@ -57,7 +57,7 @@ std::auto_ptr XmlTreeParser::Parse(const std::string &line) m_tree.ConnectNodes(); SyntaxNode *root = m_tree.GetTop(); assert(root); - m_words = util::tokenize(m_line.c_str()); + m_words = util::tokenize(m_line); return ConvertTree(*root, m_words); } diff --git a/phrase-extract/pcfg-common/xml_tree_parser.cc b/phrase-extract/pcfg-common/xml_tree_parser.cc index 6a2f3fc51..29e46a9f2 100644 --- a/phrase-extract/pcfg-common/xml_tree_parser.cc +++ b/phrase-extract/pcfg-common/xml_tree_parser.cc @@ -52,7 +52,7 @@ std::auto_ptr XmlTreeParser::Parse(const std::string &line) { // There is no XML tree. return std::auto_ptr(); } - m_words = util::tokenize(m_line.c_str()); + m_words = util::tokenize(m_line); return ConvertTree(*root, m_words); } diff --git a/phrase-extract/relax-parse-main.cpp b/phrase-extract/relax-parse-main.cpp index ec7bd25fd..5c9daa7ae 100644 --- a/phrase-extract/relax-parse-main.cpp +++ b/phrase-extract/relax-parse-main.cpp @@ -45,7 +45,7 @@ int main(int argc, char* argv[]) map< string, int > topLabelCollection; // count of top labels, not used SyntaxTree tree; ProcessAndStripXMLTags( inBufferString, tree, labelCollection, topLabelCollection, false ); - const vector< string > inWords = util::tokenize( inBufferString.c_str() ); + const vector< string > inWords = util::tokenize( inBufferString ); // output tree // cerr << "BEFORE:" << endl << tree; diff --git a/phrase-extract/statistics-main.cpp b/phrase-extract/statistics-main.cpp index d797b9f45..840f18602 100644 --- a/phrase-extract/statistics-main.cpp +++ b/phrase-extract/statistics-main.cpp @@ -322,7 +322,7 @@ void LexicalTable::load( const string &filePath ) i++; if (i%100000 == 0) cerr << "." << flush; - const vector token = util::tokenize( line.c_str() ); + const vector token = util::tokenize( line ); if (token.size() != 3) { cerr << "line " << i << " in " << filePath << " has wrong number of tokens, skipping:\n" << token.size() << " " << token[0] << " " << line << endl; diff --git a/phrase-extract/syntax-common/xml_tree_parser.cc b/phrase-extract/syntax-common/xml_tree_parser.cc index 47fda1c2d..c6e3cd3c3 100644 --- a/phrase-extract/syntax-common/xml_tree_parser.cc +++ b/phrase-extract/syntax-common/xml_tree_parser.cc @@ -25,7 +25,7 @@ StringTree *XmlTreeParser::Parse(const std::string &line) { tree_.ConnectNodes(); SyntaxNode *root = tree_.GetTop(); assert(root); - words_ = util::tokenize(line_.c_str()); + words_ = util::tokenize(line_); return ConvertTree(*root, words_); } diff --git a/phrase-extract/tables-core.cpp b/phrase-extract/tables-core.cpp index 93c5041dd..9aa7aa787 100644 --- a/phrase-extract/tables-core.cpp +++ b/phrase-extract/tables-core.cpp @@ -85,7 +85,7 @@ void DTable::load( const string& fileName ) abort(); } - const vector token = util::tokenize(line.c_str()); + const vector token = util::tokenize(line); if (token.size() < 2) { cerr << "line " << i << " in " << fileName << " too short, skipping\n"; continue; diff --git a/util/tokenize.hh b/util/tokenize.hh index f4f3289bc..5d8430222 100644 --- a/util/tokenize.hh +++ b/util/tokenize.hh @@ -37,6 +37,15 @@ inline std::vector tokenize(const char input[]) return token; } +/** Split input string into a series of tokens. + * + * Like tokenize(const char[]), but takes a std::string. + */ +inline std::vector tokenize(const std::string &input) +{ + return tokenize(input.c_str()); +} + } // namespace util #endif From fc810e363e104616400b8d4db590a1f73f24a69a Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Wed, 22 Apr 2015 10:43:15 +0700 Subject: [PATCH 42/53] Remove conflicting definition of isNonTerminal. This only affects configurations where inline functions become regular, non-weak symbols, leading to link conflicts. The extra definition was not used anywhere. The removed definition was probably less efficient. However the only functional difference was that it returned false for the empty nonterminal, i.e. "[]". --- phrase-extract/tables-core.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phrase-extract/tables-core.cpp b/phrase-extract/tables-core.cpp index 9aa7aa787..4dd8e704a 100644 --- a/phrase-extract/tables-core.cpp +++ b/phrase-extract/tables-core.cpp @@ -11,11 +11,6 @@ using namespace std; namespace MosesTraining { -bool isNonTerminal( const WORD &symbol ) -{ - return symbol.substr(0, 1) == "[" && symbol.substr(symbol.size()-1, 1) == "]"; -} - WORD_ID Vocabulary::storeIfNew( const WORD& word ) { map::iterator i = lookup.find( word ); From 02d1d9a4af94ad148521fa934e802561e9b685a3 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Wed, 22 Apr 2015 11:24:32 +0700 Subject: [PATCH 43/53] Don't work around missing popen() in MinGW. Windows does not have popen()/pclose(), so FileHandler.cpp #define's them to _popen()/_pclose(). But MinGW has similar macros built into , leading to warnings. So skip the workaround on MinGW. --- moses/TranslationModel/DynSAInclude/FileHandler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/moses/TranslationModel/DynSAInclude/FileHandler.cpp b/moses/TranslationModel/DynSAInclude/FileHandler.cpp index 9413ffd7c..ecde3c644 100644 --- a/moses/TranslationModel/DynSAInclude/FileHandler.cpp +++ b/moses/TranslationModel/DynSAInclude/FileHandler.cpp @@ -1,7 +1,9 @@ #include "FileHandler.h" #include -#ifdef WIN32 +// Workaround: plain Windows does not have popen()/pclose(). +// (MinGW already #define's them, so skip the workaround there.) +#if defined(WIN32) && !defined(__MINGW32__) #define popen(A, B) _popen(A, B) #define pclose(A) _pclose(A) #endif From 1083999d3e4447ac347b8bcbb04a0733ae8c3654 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Wed, 22 Apr 2015 12:45:41 +0700 Subject: [PATCH 44/53] Adapt test to poor Windows timer resolution. TimerTest fails on Windows unless the sleep time is set to at least a millisecond (1,000 microseconds). Keep it nice and low for other platforms though, because the sleep time is wasted. --- mert/TimerTest.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mert/TimerTest.cpp b/mert/TimerTest.cpp index d72b1c312..532e44fc1 100644 --- a/mert/TimerTest.cpp +++ b/mert/TimerTest.cpp @@ -11,7 +11,20 @@ using namespace MosesTuning; BOOST_AUTO_TEST_CASE(timer_basic_test) { Timer timer; - const int sleep_time_microsec = 40; // ad-hoc microseconds to pass unit tests. + + // Sleep time. The test will sleep for this number of microseconds, and + // expect the elapsed time to be noticeable. + // Keep this number low to avoid wasting test time sleeping, but at least as + // high as the Boost timer's resolution. Tests must pass consistently, not + // just on lucky runs. +#if defined(WIN32) + // Timer resolution on Windows seems to be a millisecond. Anything less and + // the test fails consistently. + const int sleep_time_microsec = 1000; +#else + // Unix-like systems seem to have more fine-grained clocks. + const int sleep_time_microsec = 40; +#endif timer.start(); BOOST_REQUIRE(timer.is_running()); From 75bfb758822cc926a1852c6a86a7ce5d153a1320 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Wed, 22 Apr 2015 20:43:29 +0700 Subject: [PATCH 45/53] Thread-safe, platform-agnostic randomizer. Some places in mert use srandom()/random(), but these are POSIX-specific. The standard alternative, srand()/rand(), is not thread-safe. This module wraps srand()/rand() in mutexes (very short-lived, so should not cost much) so that it relies on just Boost and the C standard library, not on a Unix-like environment. This may reduce the width of the random numbers on some platforms: it goes from "long int" to just "int". If that is a problem, we may have to use Boost's randomizer utilities, or eventually, the C++ ones. --- mert/Data.cpp | 3 ++- mert/Point.cpp | 5 +++-- mert/evaluator.cpp | 7 ++++--- mert/mert.cpp | 5 +++-- util/Jamfile | 2 +- util/random.cc | 39 +++++++++++++++++++++++++++++++++++++++ util/random.hh | 32 ++++++++++++++++++++++++++++++++ util/random_test.cc | 39 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 util/random.cc create mode 100644 util/random.hh create mode 100644 util/random_test.cc diff --git a/mert/Data.cpp b/mert/Data.cpp index 49c1239e5..428886b99 100644 --- a/mert/Data.cpp +++ b/mert/Data.cpp @@ -17,6 +17,7 @@ #include "util/exception.hh" #include "util/file_piece.hh" +#include "util/random.hh" #include "util/tokenize_piece.hh" #include "util/string_piece.hh" #include "FeatureDataIterator.h" @@ -286,7 +287,7 @@ void Data::createShards(size_t shard_count, float shard_size, const string& scor } else { //create shards by randomly sampling for (size_t i = 0; i < floor(shard_size+0.5); ++i) { - shard_contents.push_back(rand() % data_size); + shard_contents.push_back(util::rand_int() % data_size); } } diff --git a/mert/Point.cpp b/mert/Point.cpp index 55dc6a6b2..562249492 100644 --- a/mert/Point.cpp +++ b/mert/Point.cpp @@ -3,6 +3,7 @@ #include #include #include "util/exception.hh" +#include "util/random.hh" #include "FeatureStats.h" #include "Optimizer.h" @@ -58,8 +59,8 @@ void Point::Randomize() UTIL_THROW_IF(m_max.size() != Point::m_dim, util::Exception, "Error"); for (unsigned int i = 0; i < size(); i++) { - operator[](i) = m_min[i] + - static_cast(random()) / static_cast(RAND_MAX) * (m_max[i] - m_min[i]); + const float scale = (m_max[i] - m_min[i]) / float(RAND_MAX); + operator[](i) = m_min[i] + util::rand_int() * scale; } } diff --git a/mert/evaluator.cpp b/mert/evaluator.cpp index 026abf397..61775a354 100644 --- a/mert/evaluator.cpp +++ b/mert/evaluator.cpp @@ -16,6 +16,7 @@ #include "Timer.h" #include "Util.h" #include "Data.h" +#include "util/random.hh" using namespace std; using namespace MosesTuning; @@ -94,7 +95,7 @@ void EvaluatorUtil::evaluate(const string& candFile, int bootstrap, bool nbest_i for (int i = 0; i < bootstrap; ++i) { ScoreData scoredata(g_scorer); for (int j = 0; j < n; ++j) { - int randomIndex = random() % n; + int randomIndex = util::rand_int() % n; scoredata.add(entries[randomIndex], j); } g_scorer->setScoreData(&scoredata); @@ -284,10 +285,10 @@ void InitSeed(const ProgramOption *opt) { if (opt->has_seed) { cerr << "Seeding random numbers with " << opt->seed << endl; - srandom(opt->seed); + util::rand_int_init(opt->seed); } else { cerr << "Seeding random numbers with system clock " << endl; - srandom(time(NULL)); + util::rand_int_init(); } } diff --git a/mert/mert.cpp b/mert/mert.cpp index 275aa7b09..e79ba7c6b 100644 --- a/mert/mert.cpp +++ b/mert/mert.cpp @@ -24,6 +24,7 @@ #include "Types.h" #include "Timer.h" #include "Util.h" +#include "util/random.hh" #include "moses/ThreadPool.h" @@ -289,10 +290,10 @@ int main(int argc, char **argv) if (option.has_seed) { cerr << "Seeding random numbers with " << option.seed << endl; - srandom(option.seed); + util::rand_int_init(option.seed); } else { cerr << "Seeding random numbers with system clock " << endl; - srandom(time(NULL)); + util::rand_int_init(); } if (option.sparse_weights_file.size()) ++option.pdim; diff --git a/util/Jamfile b/util/Jamfile index a82a5e23d..2d3cede01 100644 --- a/util/Jamfile +++ b/util/Jamfile @@ -21,7 +21,7 @@ obj file_piece_test.o : file_piece_test.cc /top//boost_unit_test_framework : $(c fakelib parallel_read : parallel_read.cc : multi:/top//boost_thread multi:WITH_THREADS : : .. ; -fakelib kenutil : bit_packing.cc ersatz_progress.cc exception.cc file.cc file_piece.cc mmap.cc murmur_hash.cc parallel_read pool.cc read_compressed scoped.cc string_piece.cc usage.cc double-conversion//double-conversion : .. LINUX,single:rt : : .. ; +fakelib kenutil : bit_packing.cc ersatz_progress.cc exception.cc file.cc file_piece.cc mmap.cc murmur_hash.cc parallel_read pool.cc random.cc read_compressed scoped.cc string_piece.cc usage.cc double-conversion//double-conversion : .. LINUX,single:rt : : .. ; exe cat_compressed : cat_compressed_main.cc kenutil ; diff --git a/util/random.cc b/util/random.cc new file mode 100644 index 000000000..368718c9f --- /dev/null +++ b/util/random.cc @@ -0,0 +1,39 @@ +#include "util/random.hh" + +#include + +#include +#include +#include + +namespace util +{ +namespace +{ +/** Lock to protect randomizer. + * + * This module is implemented in terms of rand()/srand() from . + * These functions are standard C, but they're not thread-safe. Scalability + * is not worth much complexity here, so just slap a mutex around it. + */ +boost::mutex rand_lock; +} // namespace + +void rand_int_init(unsigned int seed) +{ + boost::lock_guard lock(rand_lock); + srand(seed); +} + + +void rand_int_init() +{ + rand_int_init(time(NULL)); +} + +int rand_int() +{ + boost::lock_guard lock(rand_lock); + return rand(); +} +} // namespace util diff --git a/util/random.hh b/util/random.hh new file mode 100644 index 000000000..c372de463 --- /dev/null +++ b/util/random.hh @@ -0,0 +1,32 @@ +#ifndef UTIL_RANDOM_H +#define UTIL_RANDOM_H + +namespace util +{ + +/** Initialize randomizer with a fixed seed. + * + * After this, unless the randomizer gets seeded again, consecutive calls to + * rand_int() will return a sequence of pseudo-random numbers determined by + * the seed. Every time the randomizer is seeded with this same seed, it will + * again start returning the same sequence of numbers. + */ +void rand_int_init(unsigned int); + +/** Initialize randomizer based on current time. + * + * Call this to make the randomizer return hard-to-predict numbers. It won't + * produce high-grade randomness, but enough to make the program act + * differently on different runs. + */ +void rand_int_init(); + +/** Return a pseudorandom number between 0 and RAND_MAX inclusive. + * + * Initialize (seed) the randomizer before starting to call this. + */ +int rand_int(); + +} // namespace util + +#endif diff --git a/util/random_test.cc b/util/random_test.cc new file mode 100644 index 000000000..d1c555a0c --- /dev/null +++ b/util/random_test.cc @@ -0,0 +1,39 @@ +#include "util/random.hh" + +#define BOOST_TEST_MODULE RandomTest +#include + +namespace util +{ +namespace +{ + +BOOST_AUTO_TEST_CASE(returns_different_consecutive_numbers) +{ + rand_int_init(99); + const int first = rand_int(), second = rand_int(), third = rand_int(); + // Sometimes you'll get the same number twice in a row, but generally the + // randomizer returns different numbers. + BOOST_CHECK(second != first || third != first); +} + +BOOST_AUTO_TEST_CASE(returns_different_numbers_for_different_seeds) +{ + rand_int_init(1); + const int one1 = rand_int(), one2 = rand_int(); + rand_int_init(2); + const int two1 = rand_int(), two2 = rand_int(); + BOOST_CHECK(two1 != one1 || two2 != two1); +} + +BOOST_AUTO_TEST_CASE(returns_same_sequence_for_same_seed) +{ + rand_int_init(1); + const int first = rand_int(); + rand_int_init(1); + const int second = rand_int(); + BOOST_CHECK_EQUAL(first, second); +} + +} // namespace +} // namespace util From 40933b4a782d43b9e07a89e62c4c6ffd1b12d1c5 Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Wed, 22 Apr 2015 19:01:12 +0400 Subject: [PATCH 46/53] hack to allow target side of tokenized parallel corpus to be used for LM --- contrib/other-builds/all.workspace | 4 +-- .../other-builds/moses-cmd/moses-cmd.project | 35 ++++++++++++++----- scripts/ems/experiment.meta | 7 ++++ 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/contrib/other-builds/all.workspace b/contrib/other-builds/all.workspace index 7b1d7862b..787fda633 100644 --- a/contrib/other-builds/all.workspace +++ b/contrib/other-builds/all.workspace @@ -7,8 +7,8 @@ - - + + diff --git a/contrib/other-builds/moses-cmd/moses-cmd.project b/contrib/other-builds/moses-cmd/moses-cmd.project index b978b451e..ecef4038b 100644 --- a/contrib/other-builds/moses-cmd/moses-cmd.project +++ b/contrib/other-builds/moses-cmd/moses-cmd.project @@ -1,5 +1,22 @@ + + + + + + + + @@ -9,6 +26,14 @@ + + + + + + + + @@ -53,7 +78,7 @@ - + @@ -125,12 +150,4 @@ - - - - - - - - diff --git a/scripts/ems/experiment.meta b/scripts/ems/experiment.meta index ead9ebe03..62f85eb1c 100644 --- a/scripts/ems/experiment.meta +++ b/scripts/ems/experiment.meta @@ -165,11 +165,18 @@ get-corpus pass-unless: get-corpus-script default-name: lm/txt template: $get-corpus-script > OUT +use-parallel-corpus + in: parallel-corpus-stem + out: tokenized-corpus + default-name: lm/tok + pass-unless: parallel-corpus-stem + template: ln -s IN.$output-extension OUT tokenize in: raw-corpus out: tokenized-corpus default-name: lm/tok pass-unless: output-tokenizer + ignore-if: parallel-corpus-stem template: $output-tokenizer < IN > OUT parallelizable: yes mock-parse From 4b47e1148c7cfe771c8e813cb9d741c2de44ed42 Mon Sep 17 00:00:00 2001 From: Hieu Hoang Date: Wed, 22 Apr 2015 23:02:57 +0400 Subject: [PATCH 47/53] use ignore-unless /Philipp Koehn --- scripts/ems/experiment.meta | 2 +- scripts/training/train-model.perl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ems/experiment.meta b/scripts/ems/experiment.meta index 62f85eb1c..57ef4f9d6 100644 --- a/scripts/ems/experiment.meta +++ b/scripts/ems/experiment.meta @@ -169,7 +169,7 @@ use-parallel-corpus in: parallel-corpus-stem out: tokenized-corpus default-name: lm/tok - pass-unless: parallel-corpus-stem + ignore-unless: parallel-corpus-stem template: ln -s IN.$output-extension OUT tokenize in: raw-corpus diff --git a/scripts/training/train-model.perl b/scripts/training/train-model.perl index fb63d4bbd..4c355479c 100755 --- a/scripts/training/train-model.perl +++ b/scripts/training/train-model.perl @@ -1,4 +1,4 @@ -#!/usr/bin/env perl +#!/usr/bin/env perl use warnings; use strict; From 7457099f51046c4f46fb4f2e1157c9710e3a7a36 Mon Sep 17 00:00:00 2001 From: Matthias Huck Date: Thu, 23 Apr 2015 17:25:02 +0100 Subject: [PATCH 48/53] SparseReordering: option to use pre-tuned feature weights internally --- .../FF/LexicalReordering/SparseReordering.cpp | 129 +++++++++++++++++- moses/FF/LexicalReordering/SparseReordering.h | 7 + 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/moses/FF/LexicalReordering/SparseReordering.cpp b/moses/FF/LexicalReordering/SparseReordering.cpp index 040b94988..6c91a8ed8 100644 --- a/moses/FF/LexicalReordering/SparseReordering.cpp +++ b/moses/FF/LexicalReordering/SparseReordering.cpp @@ -13,8 +13,11 @@ #include "LexicalReordering.h" #include "SparseReordering.h" +#include + using namespace std; +using namespace boost::algorithm; namespace Moses { @@ -57,6 +60,7 @@ const std::string& SparseReorderingFeatureKey::Name (const string& wordListId) SparseReordering::SparseReordering(const map& config, const LexicalReordering* producer) : m_producer(producer) + , m_useWeightMap(false) { static const string kSource= "source"; static const string kTarget = "target"; @@ -80,6 +84,14 @@ SparseReordering::SparseReordering(const map& config, const Lexic } else { UTIL_THROW(util::Exception, "Sparse reordering requires source or target, not " << fields[1]); } + } else if (fields[0] == "weights") { + ReadWeightMap(i->second); + m_useWeightMap = true; + for (int reoType=0; reoType<=LRModel::MAX; ++reoType) { + ostringstream buf; + buf << reoType; + m_featureMap2.push_back(m_producer->GetFeatureName(buf.str())); + } } else if (fields[0] == "phrase") { m_usePhrase = true; @@ -175,7 +187,18 @@ void SparseReordering::AddFeatures( SparseReorderingFeatureKey key(id, type, wordFactor, false, position, side, reoType); FeatureMap::const_iterator fmi = m_featureMap.find(key); assert(fmi != m_featureMap.end()); - scores->SparsePlusEquals(fmi->second, 1.0); + if (m_useWeightMap) { +// std::cerr << "fmi->second.name()= " << fmi->second.name() << std::endl; + WeightMap::const_iterator wmi = m_weightMap.find(fmi->second.name()); + if (wmi != m_weightMap.end()) { +// std::cerr << "scoring: " << buf.str() << " " << wmi->second << std::endl; + if (wmi->second != 0) { + scores->SparsePlusEquals(m_featureMap2[reoType], wmi->second); + } + } + } else { + scores->SparsePlusEquals(fmi->second, 1.0); + } } for (size_t id = 0; id < clusterMaps->size(); ++id) { @@ -186,7 +209,18 @@ void SparseReordering::AddFeatures( SparseReorderingFeatureKey key(id, type, clusterIter->second, true, position, side, reoType); FeatureMap::const_iterator fmi = m_featureMap.find(key); assert(fmi != m_featureMap.end()); - scores->SparsePlusEquals(fmi->second, 1.0); + if (m_useWeightMap) { +// std::cerr << "fmi->second.name()= " << fmi->second.name() << std::endl; + WeightMap::const_iterator wmi = m_weightMap.find(fmi->second.name()); + if (wmi != m_weightMap.end()) { +// std::cerr << "scoring: " << buf.str() << " " << wmi->second << std::endl; + if (wmi->second != 0) { + scores->SparsePlusEquals(m_featureMap2[reoType], wmi->second); + } + } + } else { + scores->SparsePlusEquals(fmi->second, 1.0); + } } } @@ -256,5 +290,96 @@ void SparseReordering::CopyScores( } + +void SparseReordering::ReadWeightMap(const string& filename) +{ + util::FilePiece file(filename.c_str()); + StringPiece line; + while (true) { + try { + line = file.ReadLine(); + } catch (const util::EndOfFileException &e) { + break; + } + util::TokenIter lineIter(line,util::SingleCharacter(' ')); + UTIL_THROW_IF2(!lineIter, "Malformed weight line: '" << line << "'"); + const std::string& name = lineIter->as_string(); + ++lineIter; + UTIL_THROW_IF2(!lineIter, "Malformed weight line: '" << line << "'"); + float weight = Moses::Scan(lineIter->as_string()); + + std::pair< WeightMap::iterator, bool> inserted = m_weightMap.insert( std::make_pair(name, weight) ); + UTIL_THROW_IF2(!inserted.second, "Duplicate weight: '" << name << "'"); + } +} + +/* +const SparseReorderingFeatureKey SparseReordering::FeatureKeyFromString(std::string& name) const +{ + std::vector tokens; + std::vector tokens = Tokenize(name, "-"); + + SparseReorderingFeatureKey key; + + UTIL_THROW_IF2(tokens.size() != 6, + "Flawed sparse reordering feature key"); + + // type + if ( tokens[0] == "phr" ) { + key.type = Phrase; + } else if ( tokens[0] == "stk" ) { + key.type = Stack; + } else if ( tokens[0] == "btn" ) { + key.type = Between; + } else { + UTIL_THROW2("Flawed sparse reordering feature key"); + } + + // side + if ( tokens[1] == "src" ) { + key.side = Source; + } else if ( tokens[1] == "tgt" ) { + key.side = Target; + } else { + UTIL_THROW2("Flawed sparse reordering feature key"); + } + + // position + if ( tokens[2] == "first" ) { + key.position = First; + } else if ( tokens[2] == "last" ) { + key.position = Last; + } else { + UTIL_THROW2("Flawed sparse reordering feature key"); + } + + // wordListId + std::string& wordListId = tokens[3]; + + // cluster/word + if (starts_with(tokens[4], "cluster_")) { + key.isCluster = true; + } else { + key.isCluster = false; + } + + + + +buf << kSep; + + if (isCluster) buf << "cluster_"; + buf << word->GetString(); + +buf << kSep; + + buf << reoType; + + + name = buf.str(); + return name; +} +*/ + } //namespace diff --git a/moses/FF/LexicalReordering/SparseReordering.h b/moses/FF/LexicalReordering/SparseReordering.h index 8a2495ce8..1aee1c4da 100644 --- a/moses/FF/LexicalReordering/SparseReordering.h +++ b/moses/FF/LexicalReordering/SparseReordering.h @@ -112,10 +112,17 @@ private: typedef boost::unordered_map FeatureMap; FeatureMap m_featureMap; + typedef boost::unordered_map WeightMap; + WeightMap m_weightMap; + bool m_useWeightMap; + std::vector m_featureMap2; + void ReadWordList(const std::string& filename, const std::string& id, SparseReorderingFeatureKey::Side side, std::vector* pWordLists); void ReadClusterMap(const std::string& filename, const std::string& id, SparseReorderingFeatureKey::Side side, std::vector* pClusterMaps); void PreCalculateFeatureNames(size_t index, const std::string& id, SparseReorderingFeatureKey::Side side, const Factor* factor, bool isCluster); + void ReadWeightMap(const std::string& filename); +// const SparseReorderingFeatureKey FeatureKeyFromString(std::string& name) const; void AddFeatures( SparseReorderingFeatureKey::Type type, SparseReorderingFeatureKey::Side side, From 38d790cac0f21de9ff8951dce0bb5d522ad46c0d Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 23 Apr 2015 19:27:21 +0700 Subject: [PATCH 49/53] Add cross-platform randomizer module. The code uses two mechanisms for generating random numbers: srand()/rand(), which is not thread-safe, and srandom()/random(), which is POSIX-specific. Here I add a util/random.cc module that centralizes these calls, and unifies some common usage patterns. If the implementation is not good enough, we can now change it in a single place. To keep things simple, this uses the portable srand()/rand() but protects them with a lock to avoid concurrency problems. The hard part was to keep the regression tests passing: they rely on fixed sequences of random numbers, so a small code change could break them very thoroughly. Util::rand(), for wide types like size_t, calls std::rand() not once but twice. This behaviour was generalized into utils::wide_rand() and friends. --- contrib/mira/Main.h | 8 +- contrib/relent-filter/src/Main.cpp | 3 +- mert/Data.cpp | 2 +- mert/Point.cpp | 6 +- mert/TODO | 7 +- mert/evaluator.cpp | 6 +- mert/kbmira.cpp | 5 +- mert/mert.cpp | 4 +- mert/pro.cpp | 9 +- moses-cmd/MainVW.cpp | 3 +- moses/ExportInterface.cpp | 5 +- moses/Manager.cpp | 3 +- moses/Parameter.cpp | 3 +- moses/TranslationModel/DynSAInclude/hash.h | 14 +- moses/TranslationModel/DynSAInclude/utils.h | 16 -- moses/TranslationModel/DynSuffixArray.cpp | 38 ++-- .../RuleTable/PhraseDictionaryFuzzyMatch.cpp | 5 +- .../UG/generic/sampling/Sampling.h | 17 +- moses/TranslationModel/UG/mm/ug_mmbitext.cc | 2 +- .../UG/mm/ug_tsa_array_entry.h | 2 +- .../UG/mm/ug_tsa_tree_iterator.h | 12 +- util/random.cc | 12 +- util/random.hh | 207 +++++++++++++++++- util/random_test.cc | 180 +++++++++++++-- 24 files changed, 452 insertions(+), 117 deletions(-) diff --git a/contrib/mira/Main.h b/contrib/mira/Main.h index 8736257f6..ffc6d0d4e 100644 --- a/contrib/mira/Main.h +++ b/contrib/mira/Main.h @@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "moses/Word.h" #include "moses/FF/FeatureFunction.h" #include "Decoder.h" +#include "util/random.hh" typedef std::map > ProducerWeightMap; typedef std::pair > ProducerWeightPair; @@ -37,8 +38,11 @@ template bool from_string(T& t, const std::string& s, std::ios_base& ( struct RandomIndex { ptrdiff_t operator()(ptrdiff_t max) { - srand(time(0)); // Initialize random number generator with current time. - return static_cast (rand() % max); + // TODO: Don't seed the randomizer here. If this function gets called + // multiple times in the same second, it will return the same value on + // each of those calls. + util::rand_init(); + return util::rand_excl(max); } }; diff --git a/contrib/relent-filter/src/Main.cpp b/contrib/relent-filter/src/Main.cpp index 1f86e2cc7..3c7911248 100755 --- a/contrib/relent-filter/src/Main.cpp +++ b/contrib/relent-filter/src/Main.cpp @@ -42,6 +42,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "RelativeEntropyCalc.h" #include "LexicalReordering.h" #include "LexicalReorderingState.h" +#include "util/random.hh" #ifdef HAVE_PROTOBUF #include "hypergraph.pb.h" @@ -205,7 +206,7 @@ int main(int argc, char** argv) //initialise random numbers - srand(time(NULL)); + rand_init(); // set up read/writing class IOWrapper* ioWrapper = GetIOWrapper(staticData); diff --git a/mert/Data.cpp b/mert/Data.cpp index 428886b99..98f6c8399 100644 --- a/mert/Data.cpp +++ b/mert/Data.cpp @@ -287,7 +287,7 @@ void Data::createShards(size_t shard_count, float shard_size, const string& scor } else { //create shards by randomly sampling for (size_t i = 0; i < floor(shard_size+0.5); ++i) { - shard_contents.push_back(util::rand_int() % data_size); + shard_contents.push_back(util::rand_excl(data_size)); } } diff --git a/mert/Point.cpp b/mert/Point.cpp index 562249492..681d3ab3e 100644 --- a/mert/Point.cpp +++ b/mert/Point.cpp @@ -58,10 +58,8 @@ void Point::Randomize() UTIL_THROW_IF(m_min.size() != Point::m_dim, util::Exception, "Error"); UTIL_THROW_IF(m_max.size() != Point::m_dim, util::Exception, "Error"); - for (unsigned int i = 0; i < size(); i++) { - const float scale = (m_max[i] - m_min[i]) / float(RAND_MAX); - operator[](i) = m_min[i] + util::rand_int() * scale; - } + for (unsigned int i = 0; i < size(); i++) + operator[](i) = util::rand_incl(m_min[i], m_max[i]); } double Point::operator*(const FeatureStats& F) const diff --git a/mert/TODO b/mert/TODO index 21b4ce04e..4ceb628d3 100644 --- a/mert/TODO +++ b/mert/TODO @@ -5,11 +5,8 @@ - check that --pairwise-ranked is compatible with all optimization metrics -- Replace the standard rand() currently used in MERT and PRO with better - random generators such as Boost's random generators (e.g., boost::mt19937). - - create a Random class to hide the details, i.e., how to generate - random numbers, which allows us to use custom random generators more - easily. +- Use better random generators in util/random.cc, e.g. boost::mt19937. + - Support plugging of custom random generators. Pros: - In MERT, you might want to use the random restarting technique to avoid diff --git a/mert/evaluator.cpp b/mert/evaluator.cpp index 61775a354..59ffaf3cd 100644 --- a/mert/evaluator.cpp +++ b/mert/evaluator.cpp @@ -95,7 +95,7 @@ void EvaluatorUtil::evaluate(const string& candFile, int bootstrap, bool nbest_i for (int i = 0; i < bootstrap; ++i) { ScoreData scoredata(g_scorer); for (int j = 0; j < n; ++j) { - int randomIndex = util::rand_int() % n; + const int randomIndex = util::rand_excl(n); scoredata.add(entries[randomIndex], j); } g_scorer->setScoreData(&scoredata); @@ -285,10 +285,10 @@ void InitSeed(const ProgramOption *opt) { if (opt->has_seed) { cerr << "Seeding random numbers with " << opt->seed << endl; - util::rand_int_init(opt->seed); + util::rand_init(opt->seed); } else { cerr << "Seeding random numbers with system clock " << endl; - util::rand_int_init(); + util::rand_init(); } } diff --git a/mert/kbmira.cpp b/mert/kbmira.cpp index 5a119e875..092176984 100644 --- a/mert/kbmira.cpp +++ b/mert/kbmira.cpp @@ -40,6 +40,7 @@ de recherches du Canada #include #include "util/exception.hh" +#include "util/random.hh" #include "BleuScorer.h" #include "HopeFearDecoder.h" @@ -122,10 +123,10 @@ int main(int argc, char** argv) if (vm.count("random-seed")) { cerr << "Initialising random seed to " << seed << endl; - srand(seed); + util::rand_init(seed); } else { cerr << "Initialising random seed from system clock" << endl; - srand(time(NULL)); + util::rand_init(); } // Initialize weights diff --git a/mert/mert.cpp b/mert/mert.cpp index e79ba7c6b..82b4cc34d 100644 --- a/mert/mert.cpp +++ b/mert/mert.cpp @@ -290,10 +290,10 @@ int main(int argc, char **argv) if (option.has_seed) { cerr << "Seeding random numbers with " << option.seed << endl; - util::rand_int_init(option.seed); + util::rand_init(option.seed); } else { cerr << "Seeding random numbers with system clock " << endl; - util::rand_int_init(); + util::rand_init(); } if (option.sparse_weights_file.size()) ++option.pdim; diff --git a/mert/pro.cpp b/mert/pro.cpp index 7660fe7d0..c0f9f7b57 100644 --- a/mert/pro.cpp +++ b/mert/pro.cpp @@ -43,6 +43,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "ScoreDataIterator.h" #include "BleuScorer.h" #include "Util.h" +#include "util/random.hh" using namespace std; using namespace MosesTuning; @@ -141,10 +142,10 @@ int main(int argc, char** argv) if (vm.count("random-seed")) { cerr << "Initialising random seed to " << seed << endl; - srand(seed); + util::rand_init(seed); } else { cerr << "Initialising random seed from system clock" << endl; - srand(time(NULL)); + util::rand_init(); } if (scoreFiles.size() == 0 || featureFiles.size() == 0) { @@ -211,11 +212,11 @@ int main(int argc, char** argv) vector scores; size_t n_translations = hypotheses.size(); for(size_t i=0; i translation1 = hypotheses[rand1]; float bleu1 = smoothedSentenceBleu(scoreDataIters[translation1.first]->operator[](translation1.second), bleuSmoothing, smoothBP); - size_t rand2 = rand() % n_translations; + size_t rand2 = util::rand_excl(n_translations); pair translation2 = hypotheses[rand2]; float bleu2 = smoothedSentenceBleu(scoreDataIters[translation2.first]->operator[](translation2.second), bleuSmoothing, smoothBP); diff --git a/moses-cmd/MainVW.cpp b/moses-cmd/MainVW.cpp index 00df3df80..1f28fa9a1 100644 --- a/moses-cmd/MainVW.cpp +++ b/moses-cmd/MainVW.cpp @@ -45,6 +45,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "moses/FF/StatefulFeatureFunction.h" #include "moses/FF/StatelessFeatureFunction.h" #include "moses/TrainingTask.h" +#include "util/random.hh" #ifdef HAVE_PROTOBUF #include "hypergraph.pb.h" @@ -117,7 +118,7 @@ int main(int argc, char** argv) //initialise random numbers - srand(time(NULL)); + util::rand_init(); // set up read/writing class IFVERBOSE(1) { diff --git a/moses/ExportInterface.cpp b/moses/ExportInterface.cpp index 00850329e..fe5f8a527 100644 --- a/moses/ExportInterface.cpp +++ b/moses/ExportInterface.cpp @@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include #include +#include "util/random.hh" #include "util/usage.hh" #ifdef WIN32 @@ -91,7 +92,7 @@ SimpleTranslationInterface::SimpleTranslationInterface(const string &mosesIni): exit(1); } - srand(time(NULL)); + util::rand_init(); } @@ -185,7 +186,7 @@ batch_run() const StaticData& staticData = StaticData::Instance(); //initialise random numbers - srand(time(NULL)); + util::rand_init(); IFVERBOSE(1) PrintUserTime("Created input-output object"); diff --git a/moses/Manager.cpp b/moses/Manager.cpp index da0661aee..01ece66c0 100644 --- a/moses/Manager.cpp +++ b/moses/Manager.cpp @@ -54,6 +54,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #endif #include "util/exception.hh" +#include "util/random.hh" using namespace std; @@ -418,7 +419,7 @@ void Manager::CalcLatticeSamples(size_t count, TrellisPathList &ret) const //cerr << endl; //draw the sample - float frandom = log((float)rand()/RAND_MAX); + const float frandom = log(util::rand_incl(0.0f, 1.0f)); size_t position = 1; float sum = candidateScores[0]; for (; position < candidateScores.size() && sum < frandom; ++position) { diff --git a/moses/Parameter.cpp b/moses/Parameter.cpp index e43c69d22..b6773fa13 100644 --- a/moses/Parameter.cpp +++ b/moses/Parameter.cpp @@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "InputFileStream.h" #include "StaticData.h" #include "util/exception.hh" +#include "util/random.hh" #include @@ -1392,7 +1393,7 @@ struct Credit { this->contact = contact ; this->currentPursuits = currentPursuits ; this->areaResponsibility = areaResponsibility; - this->sortId = rand() % 1000; + this->sortId = util::rand_excl(1000); } bool operator<(const Credit &other) const { diff --git a/moses/TranslationModel/DynSAInclude/hash.h b/moses/TranslationModel/DynSAInclude/hash.h index 8536c46f5..4cf69bf2f 100644 --- a/moses/TranslationModel/DynSAInclude/hash.h +++ b/moses/TranslationModel/DynSAInclude/hash.h @@ -6,6 +6,7 @@ #include "utils.h" #include "FileHandler.h" #include "util/exception.hh" +#include "util/random.hh" using namespace Moses; typedef uint64_t P; // largest input range is 2^64 @@ -162,7 +163,7 @@ void Hash_shiftAddXOR::initSeeds() { v_ = new T[this->H_]; for(count_t i=0; i < this->H_; i++) - v_[i] = Utils::rand() + 1; + v_[i] = util::wide_rand() + 1; } template T Hash_shiftAddXOR::hash(const char* s, count_t h) @@ -187,9 +188,8 @@ void UnivHash_tableXOR::initSeeds() // fill with random values for(count_t j=0; j < this->H_; j++) { table_[j] = new T[tblLen_]; - for(count_t i=0; i < tblLen_; i++) { - table_[j][i] = Utils::rand(this->m_-1); - } + for(count_t i=0; i < tblLen_; i++) + table_[j][i] = util::wide_rand_excl(this->m_-1); } } template @@ -218,7 +218,7 @@ void UnivHash_noPrimes::initSeeds() { a_ = new P[this->H_]; for(T i=0; i < this->H_; i++) { - a_[i] = Utils::rand

(); + a_[i] = util::wide_rand

(); if(a_[i] % 2 == 0) a_[i]++; // a must be odd } } @@ -284,8 +284,8 @@ void UnivHash_linear::initSeeds() a_[i] = new T[MAX_NGRAM_ORDER]; b_[i] = new T[MAX_NGRAM_ORDER]; for(count_t j=0; j < MAX_NGRAM_ORDER; j++) { - a_[i][j] = 1 + Utils::rand(); - b_[i][j] = Utils::rand(); + a_[i][j] = 1 + util::wide_rand(); + b_[i][j] = util::wide_rand(); } } } diff --git a/moses/TranslationModel/DynSAInclude/utils.h b/moses/TranslationModel/DynSAInclude/utils.h index e2f24fd4f..485e4a065 100644 --- a/moses/TranslationModel/DynSAInclude/utils.h +++ b/moses/TranslationModel/DynSAInclude/utils.h @@ -62,22 +62,6 @@ public: str[i] = tolower(str[i]); } } - // TODO: interface with decent PRG - template - static T rand(T mod_bnd = 0) { - T random = 0; - if(sizeof(T) <= 4) { - random = static_cast(std::rand()); - } else if(sizeof(T) == 8) { - random = static_cast(std::rand()); - random <<= 31; - random <<= 1; - random |= static_cast(std::rand()); - } - if(mod_bnd != 0) - return random % mod_bnd; - else return random; - } }; #endif diff --git a/moses/TranslationModel/DynSuffixArray.cpp b/moses/TranslationModel/DynSuffixArray.cpp index 3e8c79c0e..c1dc62f12 100644 --- a/moses/TranslationModel/DynSuffixArray.cpp +++ b/moses/TranslationModel/DynSuffixArray.cpp @@ -1,4 +1,6 @@ #include "DynSuffixArray.h" +#include "util/random.hh" + #include #include @@ -315,33 +317,31 @@ int DynSuffixArray::Compare(int pos1, int pos2, int max) return 0; } +namespace +{ +/// Helper: swap two entries in an int array. +inline void swap_ints(int array[], int one, int other) +{ + const int tmp = array[one]; + array[one] = array[other]; + array[other] = tmp; +} +} + void DynSuffixArray::Qsort(int* array, int begin, int end) { if(end > begin) { - int index; + int index = util::rand_incl(begin, end); { - index = begin + (rand() % (end - begin + 1)); - int pivot = array[index]; - { - int tmp = array[index]; - array[index] = array[end]; - array[end] = tmp; - } + const int pivot = array[index]; + swap_ints(array, index, end); for(int i=index=begin; i < end; ++i) { if (Compare(array[i], pivot, 20) <= 0) { - { - int tmp = array[index]; - array[index] = array[i]; - array[i] = tmp; - index++; - } + swap_ints(array, index, i); + index++; } } - { - int tmp = array[index]; - array[index] = array[end]; - array[end] = tmp; - } + swap_ints(array, index, end); } Qsort(array, begin, index - 1); Qsort(array, index + 1, end); diff --git a/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp b/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp index dc25b805b..d244f9d8e 100644 --- a/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp +++ b/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp @@ -45,6 +45,7 @@ #include "moses/TranslationModel/fuzzy-match/SentenceAlignment.h" #include "util/file.hh" #include "util/exception.hh" +#include "util/random.hh" using namespace std; @@ -62,8 +63,8 @@ char *mkdtemp(char *tempbuf) return NULL; } - srand((unsigned)time(0)); - rand_value = (int)((rand() / ((double)RAND_MAX+1.0)) * 1e6); + util::rand_init(); + rand_value = rand_excl(1e6); tempbase = strrchr(tempbuf, '/'); tempbase = tempbase ? tempbase+1 : tempbuf; strcpy(tempbasebuf, tempbase); diff --git a/moses/TranslationModel/UG/generic/sampling/Sampling.h b/moses/TranslationModel/UG/generic/sampling/Sampling.h index c60953d5d..652e532bc 100644 --- a/moses/TranslationModel/UG/generic/sampling/Sampling.h +++ b/moses/TranslationModel/UG/generic/sampling/Sampling.h @@ -2,19 +2,16 @@ #define __sampling_h #include #include + +#include "util/random.hh" + // Utility functions for proper sub-sampling. // (c) 2007-2012 Ulrich Germann namespace Moses { - using namespace std; -inline -size_t -randInt(size_t N) -{ - return N*(rand()/(RAND_MAX+1.)); -} +using namespace std; // select a random sample of size /s/ without restitution from the range of // integers [0,N); @@ -35,15 +32,15 @@ randomSample(vector& v, size_t s, size_t N) if (s*10 check(N,0); for (size_t i = 0; i < v.size(); i++) { - size_t x = randInt(N); - while (check[x]) x = randInt(N); + size_t x = util::rand_excl(N); + while (check[x]) x = util::rand_excl(N); check[x]=true; v[i] = x; } } else { size_t m=0; for (size_t t = 0; m <= s && t < N; t++) - if (s==N || randInt(N-t) < s-m) v[m++] = t; + if (s==N || util::rand_excl(N-t) < s-m) v[m++] = t; } } diff --git a/moses/TranslationModel/UG/mm/ug_mmbitext.cc b/moses/TranslationModel/UG/mm/ug_mmbitext.cc index 8f1a4aa12..2c00665bb 100644 --- a/moses/TranslationModel/UG/mm/ug_mmbitext.cc +++ b/moses/TranslationModel/UG/mm/ug_mmbitext.cc @@ -345,7 +345,7 @@ // { // boost::lock_guard lock(stats->lock); // if (stats->raw_cnt == ctr) ++stats->raw_cnt; -// size_t rnum = randInt(stats->raw_cnt - ctr++); +// size_t rnum = util::rand_excl(stats->raw_cnt - ctr++); // // cout << stats->raw_cnt << " " << ctr-1 << " " // // << rnum << " " << max_samples - stats->good << endl; // if (rnum < max_samples - stats->good) diff --git a/moses/TranslationModel/UG/mm/ug_tsa_array_entry.h b/moses/TranslationModel/UG/mm/ug_tsa_array_entry.h index fc4b9f0ad..034a74bd9 100644 --- a/moses/TranslationModel/UG/mm/ug_tsa_array_entry.h +++ b/moses/TranslationModel/UG/mm/ug_tsa_array_entry.h @@ -69,7 +69,7 @@ namespace ugdiss // while (chosen < samplesize && next < stop) // { // root->readEntry(next,*this); - // if (randInt(N - sampled++) < samplesize - chosen) + // if (util::rand_excl(N - sampled++) < samplesize - chosen) // { // ++chosen; // return true; diff --git a/moses/TranslationModel/UG/mm/ug_tsa_tree_iterator.h b/moses/TranslationModel/UG/mm/ug_tsa_tree_iterator.h index ac8cbe24e..1f27f9c61 100644 --- a/moses/TranslationModel/UG/mm/ug_tsa_tree_iterator.h +++ b/moses/TranslationModel/UG/mm/ug_tsa_tree_iterator.h @@ -9,6 +9,7 @@ #include #include "util/exception.hh" #include "moses/Util.h" +#include "util/random.hh" //#include // #include "ug_bv_iter.h" @@ -894,13 +895,6 @@ namespace ugdiss return bv; } - inline - size_t - randInt(size_t N) - { - return size_t(N*(rand()/(RAND_MAX+1.))); - } - /// randomly select up to N occurrences of the sequence template sptr > @@ -922,8 +916,8 @@ namespace ugdiss root->readEntry(I.next,I); // t: expected number of remaining samples - double t = (stop - I.pos)/root->aveIndexEntrySize(); - double r = t*rand()/(RAND_MAX+1.); + const double t = (stop - I.pos)/root->aveIndexEntrySize(); + const double r = util::rand_excl(t); if (r < N-m) { ret->at(m).offset = I.offset; diff --git a/util/random.cc b/util/random.cc index 368718c9f..4db1a61ee 100644 --- a/util/random.cc +++ b/util/random.cc @@ -19,21 +19,25 @@ namespace boost::mutex rand_lock; } // namespace -void rand_int_init(unsigned int seed) +void rand_init(unsigned int seed) { boost::lock_guard lock(rand_lock); srand(seed); } -void rand_int_init() +void rand_init() { - rand_int_init(time(NULL)); + rand_init(time(NULL)); } +namespace internal +{ +// This is the one call to the actual randomizer. All else is built on this. int rand_int() { boost::lock_guard lock(rand_lock); - return rand(); + return std::rand(); } +} // namespace internal } // namespace util diff --git a/util/random.hh b/util/random.hh index c372de463..6c2773520 100644 --- a/util/random.hh +++ b/util/random.hh @@ -1,32 +1,229 @@ #ifndef UTIL_RANDOM_H #define UTIL_RANDOM_H +#include +#include + namespace util { +/** Thread-safe, cross-platform random number generator. + * + * This is not for proper security-grade randomness, but should be "good + * enough" for producing arbitrary values of various numeric types. + * + * Before starting, call rand_init() to seed the randomizer. There is no need + * to do this more than once; in fact doing it more often is likely to make the + * randomizer less effective. Once that is done, call the rand(), rand_excl(), + * and rand_incl() functions as needed to generate pseudo-random numbers. + * + * Probability distribution is roughly uniform, but for integral types is + * skewed slightly towards lower numbers depending on how close "top" comes to + * RAND_MAX. + * + * For floating-point types, resolution is limited; there will actually be + * only RAND_MAX different possible values. + */ /** Initialize randomizer with a fixed seed. * * After this, unless the randomizer gets seeded again, consecutive calls to - * rand_int() will return a sequence of pseudo-random numbers determined by - * the seed. Every time the randomizer is seeded with this same seed, it will - * again start returning the same sequence of numbers. + * the random functions will return a sequence of pseudo-random numbers + * determined by the seed. Every time the randomizer is seeded with this same + * seed, it will again start returning the same sequence of numbers. */ -void rand_int_init(unsigned int); +void rand_init(unsigned int); /** Initialize randomizer based on current time. * * Call this to make the randomizer return hard-to-predict numbers. It won't * produce high-grade randomness, but enough to make the program act * differently on different runs. + * + * The seed will be based on the current time in seconds. So calling it twice + * within the same second will just reset the randomizer to where it was before. + * Don't do that. */ -void rand_int_init(); +void rand_init(); + /** Return a pseudorandom number between 0 and RAND_MAX inclusive. * * Initialize (seed) the randomizer before starting to call this. */ +template inline T rand(); + + +/** Return a pseudorandom number in the half-open interval [bottom, top). + * + * Generates a value between "bottom" (inclusive) and "top" (exclusive), + * assuming that (top - bottom) <= RAND_MAX. + */ +template inline T rand_excl(T bottom, T top); + + +/** Return a pseudorandom number in the half-open interval [0, top). + * + * Generates a value between 0 (inclusive) and "top" (exclusive), assuming that + * bottom <= RAND_MAX. + */ +template inline T rand_excl(T top); + + +/** Return a pseudorandom number in the open interval [bottom, top]. + * + * Generates a value between "bottom" and "top" inclusive, assuming that + * (top - bottom) < RAND_MAX. + */ +template inline T rand_incl(T bottom, T top); + + +/** Return a pseudorandom number in the open interval [0, top]. + * + * Generates a value between 0 and "top" inclusive, assuming that + * bottom < RAND_MAX. + */ +template inline T rand_incl(T top); + + +/** Return a pseudorandom number which may be larger than RAND_MAX. + * + * The requested type must be integral, and its size must be an even multiple + * of the size of an int. The return value will combine one or more random + * ints into a single value, which could get quite large. + * + * The result is nonnegative. Because the constituent ints are also + * nonnegative, the most significant bit in each of the ints will be zero, + * so for a wider type, there will be "gaps" in the range of possible outputs. + */ +template inline T wide_rand(); + +/** Return a pseudorandom number in [0, top), not limited to RAND_MAX. + * + * Works like wide_rand(), but if the requested type is wider than an int, it + * accommodates larger top values than an int can represent. + */ +template inline T wide_rand_excl(T top); + +/** Return a pseudorandom number in [bottom, top), not limited to RAND_MAX. + * + * Works like wide_rand(), but if the requested type is wider than an int, it + * accommodates larger value ranges than an int can represent. + */ +template inline T wide_rand_excl(T bottom, T top); + +/** Return a pseudorandom number in [0, top], not limited to RAND_MAX. + * + * Works like wide_rand(), but if the requested type is wider than an int, it + * accommodates larger top values than an int can represent. + */ +template inline T wide_rand_incl(T top); + +/** Return a pseudorandom number in [bottom, top], not limited to RAND_MAX. + * + * Works like wide_rand(), but if the requested type is wider than an int, it + * accommodates larger top values than an int can represent. + */ +template inline T wide_rand_incl(T bottom, T top); + + +/// Implementation detail. For the random module's internal use only. +namespace internal +{ +/// The central call to the randomizer upon which this whole module is built. int rand_int(); +/// Helper template: customize random values to required ranges. +template struct random_scaler; + +/// Specialized random_scaler for integral types. +template struct random_scaler +{ + static T rnd_excl(T value, T range) { return value % range; } + static T rnd_incl(T value, T range) { return value % (range + 1); } +}; + +/// Specialized random_scaler for non-integral types. +template struct random_scaler +{ + static T rnd_excl(T value, T range) + { + // Promote RAND_MAX to T before adding one to avoid overflow. + return range * value / (T(RAND_MAX) + 1); + } + static T rnd_incl(T value, T range) { return range * value / RAND_MAX; } +}; + +/// Helper for filling a wider variable with random ints. +template struct wide_random_collector +{ + static T generate() + { + T one_int = util::rand() << (8 * sizeof(int)); + return one_int | wide_random_collector::generate(); + } +}; +/// Specialized wide_random_collector for generating just a single int. +template struct wide_random_collector +{ + static T generate() { return util::rand(); } +}; + +} // namespace internal + + +template inline T rand() +{ + return T(util::internal::rand_int()); +} + +template inline T rand_excl(T top) +{ + typedef internal::random_scaler::is_integer> scaler; + return scaler::rnd_excl(util::rand(), top); +} + +template inline T rand_excl(T bottom, T top) +{ + return bottom + rand_excl(top - bottom); +} + +template inline T rand_incl(T top) +{ + typedef internal::random_scaler::is_integer> scaler; + return scaler::rnd_incl(util::rand(), top); +} + +template inline T rand_incl(T bottom, T top) +{ + return bottom + rand_incl(top - bottom); +} + +template inline T wide_rand() +{ + return internal::wide_random_collector::generate(); +} + +template inline T wide_rand_excl(T top) +{ + typedef internal::random_scaler::is_integer> scaler; + return scaler::rnd_excl(util::wide_rand(), top); +} + +template inline T wide_rand_excl(T bottom, T top) +{ + return bottom + wide_rand_excl(top - bottom); +} + +template inline T wide_rand_incl(T top) +{ + typedef internal::random_scaler::is_integer> scaler; + return scaler::rnd_incl(util::wide_rand(), top); +} + +template inline T wide_rand_incl(T bottom, T top) +{ + return bottom + wide_rand_incl(top - bottom); +} } // namespace util #endif diff --git a/util/random_test.cc b/util/random_test.cc index d1c555a0c..6d8981de8 100644 --- a/util/random_test.cc +++ b/util/random_test.cc @@ -1,3 +1,5 @@ +#include + #include "util/random.hh" #define BOOST_TEST_MODULE RandomTest @@ -8,32 +10,182 @@ namespace util namespace { -BOOST_AUTO_TEST_CASE(returns_different_consecutive_numbers) +BOOST_AUTO_TEST_CASE(rand_int_returns_positive_no_greater_than_RAND_MAX) { - rand_int_init(99); - const int first = rand_int(), second = rand_int(), third = rand_int(); + rand_init(); + for (int i=0; i<100; i++) + { + const int random_number = rand(); + BOOST_CHECK(random_number >= 0); + BOOST_CHECK(random_number <= RAND_MAX); + } +} + +BOOST_AUTO_TEST_CASE(rand_int_returns_different_consecutive_numbers) +{ + rand_init(99); + const int first = rand(), second = rand(), third = rand(); // Sometimes you'll get the same number twice in a row, but generally the // randomizer returns different numbers. BOOST_CHECK(second != first || third != first); } -BOOST_AUTO_TEST_CASE(returns_different_numbers_for_different_seeds) +BOOST_AUTO_TEST_CASE(rand_int_returns_different_numbers_for_different_seeds) { - rand_int_init(1); - const int one1 = rand_int(), one2 = rand_int(); - rand_int_init(2); - const int two1 = rand_int(), two2 = rand_int(); - BOOST_CHECK(two1 != one1 || two2 != two1); + rand_init(1); + const int one1 = rand(), one2 = rand(); + rand_init(2); + const int two1 = rand(), two2 = rand(); + BOOST_CHECK(two1 != one1 || two2 != one2); } -BOOST_AUTO_TEST_CASE(returns_same_sequence_for_same_seed) +BOOST_AUTO_TEST_CASE(rand_int_returns_same_sequence_for_same_seed) { - rand_int_init(1); - const int first = rand_int(); - rand_int_init(1); - const int second = rand_int(); + rand_init(1); + const int first = rand(); + rand_init(1); + const int second = rand(); BOOST_CHECK_EQUAL(first, second); } +BOOST_AUTO_TEST_CASE(rand_excl_int_returns_number_in_range) +{ + const int bottom = 10, top = 50; + for (int i=0; i<100; i++) + { + const int random_number = rand_excl(bottom, top); + BOOST_CHECK(random_number >= bottom); + BOOST_CHECK(random_number < top); + } +} + +BOOST_AUTO_TEST_CASE(rand_excl_int_covers_full_range) +{ + // The spread of random numbers really goes all the way from 0 (inclusive) + // to "top" (exclusive). It's not some smaller subset. + // This test will randomly fail sometimes, though very very rarely, when the + // random numbers don't actually have enough different values. + const int bottom = 1, top = 4; + int lowest = 99, highest = -1; + for (int i=0; i<100; i++) + { + const int random_number = rand_excl(bottom, top); + lowest = std::min(lowest, random_number); + highest = std::max(highest, random_number); + } + + BOOST_CHECK_EQUAL(lowest, bottom); + BOOST_CHECK_EQUAL(highest, top - 1); +} + +BOOST_AUTO_TEST_CASE(rand_incl_int_returns_number_in_range) +{ + const int bottom = 10, top = 50; + for (int i=0; i<100; i++) + { + const int random_number = rand_incl(bottom, top); + BOOST_CHECK(random_number >= 0); + BOOST_CHECK(random_number <= top); + } +} + +BOOST_AUTO_TEST_CASE(rand_incl_int_covers_full_range) +{ + // The spread of random numbers really goes all the way from 0 to "top" + // inclusive. It's not some smaller subset. + // This test will randomly fail sometimes, though very very rarely, when the + // random numbers don't actually have enough different values. + const int bottom = 1, top = 4; + int lowest = 99, highest = -1; + for (int i=0; i<100; i++) + { + const int random_number = rand_incl(bottom, top); + lowest = std::min(lowest, random_number); + highest = std::max(highest, random_number); + } + + BOOST_CHECK_EQUAL(lowest, bottom); + BOOST_CHECK_EQUAL(highest, top); +} + +BOOST_AUTO_TEST_CASE(rand_excl_float_returns_float_in_range) +{ + const float bottom = 5, top = 10; + for (int i=0; i<100; i++) + { + const float random_number = rand_excl(bottom, top); + BOOST_CHECK(random_number >= bottom); + BOOST_CHECK(random_number < top); + } +} + +BOOST_AUTO_TEST_CASE(rand_excl_float_returns_different_values) +{ + const float bottom = 5, top = 10; + float lowest = 99, highest = -1; + for (int i=0; i<10; i++) + { + const float random_number = rand_excl(bottom, top); + lowest = std::min(lowest, random_number); + highest = std::max(highest, random_number); + } + BOOST_CHECK(lowest < highest); +} + +BOOST_AUTO_TEST_CASE(rand_float_incl_returns_float_in_range) +{ + const float bottom = 5, top = 10; + for (int i=0; i<1000; i++) + { + const float random_number = rand_excl(bottom, top); + BOOST_CHECK(random_number >= bottom); + BOOST_CHECK(random_number <= top); + } +} + +BOOST_AUTO_TEST_CASE(rand_float_incl_returns_different_values) +{ + const float bottom = 0, top = 10; + float lowest = 99, highest = -1; + for (int i=0; i<10; i++) + { + const float random_number = rand_excl(bottom, top); + lowest = std::min(lowest, random_number); + highest = std::max(highest, random_number); + } + BOOST_CHECK(lowest < highest); +} + +BOOST_AUTO_TEST_CASE(wide_rand_int_returns_different_numbers_in_range) +{ + for (int i=0; i<100; i++) + { + const int random_number = wide_rand(); + BOOST_CHECK(random_number >= 0); + BOOST_CHECK(random_number <= RAND_MAX); + } +} + +BOOST_AUTO_TEST_CASE(wide_rand_long_long_returns_big_numbers) +{ + long long one = wide_rand(), two = wide_rand(); + // This test will fail sometimes because of unlucky random numbers, but only + // very very rarely. + BOOST_CHECK(one > RAND_MAX || two > RAND_MAX); +} + +BOOST_AUTO_TEST_CASE(wide_rand_excl_supports_larger_range) +{ + const long long top = 1000 * (long long)RAND_MAX; + long long + one = wide_rand_excl(top), + two = wide_rand_excl(top); + BOOST_CHECK(one < top); + BOOST_CHECK(two < top); + // This test will fail sometimes because of unlucky random numbers, but only + // very very rarely. + BOOST_CHECK(one > RAND_MAX || two > RAND_MAX); +} + } // namespace } // namespace util From f24f31f965abd3b3e02b48f7aaaf17a8003a4b8f Mon Sep 17 00:00:00 2001 From: Matthias Huck Date: Thu, 23 Apr 2015 18:13:02 +0100 Subject: [PATCH 50/53] n-best list creation in phrase-based decoding: improved efficiency with sparse features --- contrib/server/mosesserver.cpp | 2 +- moses/Manager.cpp | 2 +- moses/TrellisPath.cpp | 40 +++++++++++++++++++++-------- moses/TrellisPath.h | 14 +++++----- moses/mbr.cpp | 4 +-- moses/server/TranslationRequest.cpp | 2 +- 6 files changed, 40 insertions(+), 24 deletions(-) diff --git a/contrib/server/mosesserver.cpp b/contrib/server/mosesserver.cpp index 30c0d4299..b79c06a24 100644 --- a/contrib/server/mosesserver.cpp +++ b/contrib/server/mosesserver.cpp @@ -524,7 +524,7 @@ public: { // should the score breakdown be reported in a more structured manner? ostringstream buf; - path.GetScoreBreakdown().OutputAllFeatureScores(buf); + path.GetScoreBreakdown()->OutputAllFeatureScores(buf); nBestXMLItem["fvals"] = xmlrpc_c::value_string(buf.str()); } diff --git a/moses/Manager.cpp b/moses/Manager.cpp index da0661aee..c46183b11 100644 --- a/moses/Manager.cpp +++ b/moses/Manager.cpp @@ -1637,7 +1637,7 @@ void Manager::OutputNBest(std::ostream& out out << " |||"; // print scores with feature names - path.GetScoreBreakdown().OutputAllFeatureScores(out ); + path.GetScoreBreakdown()->OutputAllFeatureScores(out); // total out << " ||| " << path.GetTotalScore(); diff --git a/moses/TrellisPath.cpp b/moses/TrellisPath.cpp index e76adc2db..36397e006 100644 --- a/moses/TrellisPath.cpp +++ b/moses/TrellisPath.cpp @@ -31,7 +31,6 @@ namespace Moses TrellisPath::TrellisPath(const Hypothesis *hypo) : m_prevEdgeChanged(NOT_FOUND) { - m_scoreBreakdown = hypo->GetScoreBreakdown(); m_totalScore = hypo->GetTotalScore(); // enumerate path using prevHypo @@ -41,10 +40,9 @@ TrellisPath::TrellisPath(const Hypothesis *hypo) } } -void TrellisPath::InitScore() +void TrellisPath::InitTotalScore() { m_totalScore = m_path[0]->GetWinningHypo()->GetTotalScore(); - m_scoreBreakdown= m_path[0]->GetWinningHypo()->GetScoreBreakdown(); //calc score size_t sizePath = m_path.size(); @@ -53,12 +51,8 @@ void TrellisPath::InitScore() const Hypothesis *winningHypo = hypo->GetWinningHypo(); if (hypo != winningHypo) { m_totalScore = m_totalScore - winningHypo->GetTotalScore() + hypo->GetTotalScore(); - m_scoreBreakdown.MinusEquals(winningHypo->GetScoreBreakdown()); - m_scoreBreakdown.PlusEquals(hypo->GetScoreBreakdown()); } } - - } TrellisPath::TrellisPath(const TrellisPath ©, size_t edgeIndex, const Hypothesis *arc) @@ -80,7 +74,7 @@ TrellisPath::TrellisPath(const TrellisPath ©, size_t edgeIndex, const Hypoth prevHypo = prevHypo->GetPrevHypo(); } - InitScore(); + InitTotalScore(); } TrellisPath::TrellisPath(const vector edges) @@ -88,9 +82,7 @@ TrellisPath::TrellisPath(const vector edges) { m_path.resize(edges.size()); copy(edges.rbegin(),edges.rend(),m_path.begin()); - InitScore(); - - + InitTotalScore(); } @@ -172,6 +164,32 @@ void TrellisPath::CreateDeviantPaths(TrellisPathList &pathColl) const } } +const boost::shared_ptr TrellisPath::GetScoreBreakdown() const +{ + if (!m_scoreBreakdown) { + float totalScore = m_path[0]->GetWinningHypo()->GetTotalScore(); // calculated for sanity check only + + m_scoreBreakdown = boost::shared_ptr(new ScoreComponentCollection()); + m_scoreBreakdown->PlusEquals(ScoreComponentCollection(m_path[0]->GetWinningHypo()->GetScoreBreakdown())); + + //calc score + size_t sizePath = m_path.size(); + for (size_t pos = 0 ; pos < sizePath ; pos++) { + const Hypothesis *hypo = m_path[pos]; + const Hypothesis *winningHypo = hypo->GetWinningHypo(); + if (hypo != winningHypo) { + totalScore = totalScore - winningHypo->GetTotalScore() + hypo->GetTotalScore(); + m_scoreBreakdown->MinusEquals(winningHypo->GetScoreBreakdown()); + m_scoreBreakdown->PlusEquals(hypo->GetScoreBreakdown()); + } + } + + assert(totalScore == m_totalScore); + } + + return m_scoreBreakdown; +} + Phrase TrellisPath::GetTargetPhrase() const { Phrase targetPhrase(ARRAY_SIZE_INCR); diff --git a/moses/TrellisPath.h b/moses/TrellisPath.h index def86549b..89efb32e4 100644 --- a/moses/TrellisPath.h +++ b/moses/TrellisPath.h @@ -19,14 +19,14 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ***********************************************************************/ -#ifndef moses_TrellisPath_h -#define moses_TrellisPath_h +#pragma once #include #include #include #include "Hypothesis.h" #include "TypeDef.h" +#include namespace Moses { @@ -50,13 +50,13 @@ protected: , or NOT_FOUND if this path is the best trans so consist of only hypos */ - ScoreComponentCollection m_scoreBreakdown; float m_totalScore; + mutable boost::shared_ptr m_scoreBreakdown; //Used by Manager::LatticeSample() explicit TrellisPath(const std::vector edges); - void InitScore(); + void InitTotalScore(); public: TrellisPath(); // not implemented @@ -91,9 +91,7 @@ public: //! create a list of next best paths by wiggling 1 of the node at a time. void CreateDeviantPaths(TrellisPathList &pathColl) const; - inline const ScoreComponentCollection &GetScoreBreakdown() const { - return m_scoreBreakdown; - } + const boost::shared_ptr GetScoreBreakdown() const; //! get target words range of the hypo within n-best trellis. not necessarily the same as hypo.GetCurrTargetWordsRange() WordsRange GetTargetWordsRange(const Hypothesis &hypo) const; @@ -123,4 +121,4 @@ inline std::ostream& operator<<(std::ostream& out, const TrellisPath& path) } } -#endif + diff --git a/moses/mbr.cpp b/moses/mbr.cpp index df2313b66..66dac47f7 100644 --- a/moses/mbr.cpp +++ b/moses/mbr.cpp @@ -105,13 +105,13 @@ const TrellisPath doMBR(const TrellisPathList& nBestList) for (iter = nBestList.begin() ; iter != nBestList.end() ; ++iter) { const TrellisPath &path = **iter; float score = StaticData::Instance().GetMBRScale() - * path.GetScoreBreakdown().GetWeightedScore(); + * path.GetScoreBreakdown()->GetWeightedScore(); if (maxScore < score) maxScore = score; } for (iter = nBestList.begin() ; iter != nBestList.end() ; ++iter) { const TrellisPath &path = **iter; - joint_prob = UntransformScore(StaticData::Instance().GetMBRScale() * path.GetScoreBreakdown().GetWeightedScore() - maxScore); + joint_prob = UntransformScore(StaticData::Instance().GetMBRScale() * path.GetScoreBreakdown()->GetWeightedScore() - maxScore); marginal += joint_prob; joint_prob_vec.push_back(joint_prob); diff --git a/moses/server/TranslationRequest.cpp b/moses/server/TranslationRequest.cpp index 1953a711f..a1daf8393 100644 --- a/moses/server/TranslationRequest.cpp +++ b/moses/server/TranslationRequest.cpp @@ -166,7 +166,7 @@ namespace MosesServer { // should the score breakdown be reported in a more structured manner? ostringstream buf; - path->GetScoreBreakdown().OutputAllFeatureScores(buf); + path->GetScoreBreakdown()->OutputAllFeatureScores(buf); nBestXmlItem["fvals"] = xmlrpc_c::value_string(buf.str()); } From 8ac91c8d97f9ce966243c3052014de0316bce947 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Fri, 24 Apr 2015 00:22:25 +0700 Subject: [PATCH 51/53] Fix unqualified call to rand_excl(). The call needed to be made explicitly to util::rand_excl(). Sorry. --- moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp b/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp index d244f9d8e..9135b7e73 100644 --- a/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp +++ b/moses/TranslationModel/RuleTable/PhraseDictionaryFuzzyMatch.cpp @@ -64,7 +64,7 @@ char *mkdtemp(char *tempbuf) } util::rand_init(); - rand_value = rand_excl(1e6); + rand_value = util::rand_excl(1e6); tempbase = strrchr(tempbuf, '/'); tempbase = tempbase ? tempbase+1 : tempbuf; strcpy(tempbasebuf, tempbase); From 55a4789a8b31693a34f68955d0f7acab5356e227 Mon Sep 17 00:00:00 2001 From: Matthias Huck Date: Thu, 23 Apr 2015 18:38:01 +0100 Subject: [PATCH 52/53] cleanup --- .../FF/LexicalReordering/SparseReordering.cpp | 71 ------------------- moses/FF/LexicalReordering/SparseReordering.h | 1 - 2 files changed, 72 deletions(-) diff --git a/moses/FF/LexicalReordering/SparseReordering.cpp b/moses/FF/LexicalReordering/SparseReordering.cpp index 6c91a8ed8..27e090ccd 100644 --- a/moses/FF/LexicalReordering/SparseReordering.cpp +++ b/moses/FF/LexicalReordering/SparseReordering.cpp @@ -188,10 +188,8 @@ void SparseReordering::AddFeatures( FeatureMap::const_iterator fmi = m_featureMap.find(key); assert(fmi != m_featureMap.end()); if (m_useWeightMap) { -// std::cerr << "fmi->second.name()= " << fmi->second.name() << std::endl; WeightMap::const_iterator wmi = m_weightMap.find(fmi->second.name()); if (wmi != m_weightMap.end()) { -// std::cerr << "scoring: " << buf.str() << " " << wmi->second << std::endl; if (wmi->second != 0) { scores->SparsePlusEquals(m_featureMap2[reoType], wmi->second); } @@ -210,10 +208,8 @@ void SparseReordering::AddFeatures( FeatureMap::const_iterator fmi = m_featureMap.find(key); assert(fmi != m_featureMap.end()); if (m_useWeightMap) { -// std::cerr << "fmi->second.name()= " << fmi->second.name() << std::endl; WeightMap::const_iterator wmi = m_weightMap.find(fmi->second.name()); if (wmi != m_weightMap.end()) { -// std::cerr << "scoring: " << buf.str() << " " << wmi->second << std::endl; if (wmi->second != 0) { scores->SparsePlusEquals(m_featureMap2[reoType], wmi->second); } @@ -313,73 +309,6 @@ void SparseReordering::ReadWeightMap(const string& filename) } } -/* -const SparseReorderingFeatureKey SparseReordering::FeatureKeyFromString(std::string& name) const -{ - std::vector tokens; - std::vector tokens = Tokenize(name, "-"); - - SparseReorderingFeatureKey key; - - UTIL_THROW_IF2(tokens.size() != 6, - "Flawed sparse reordering feature key"); - - // type - if ( tokens[0] == "phr" ) { - key.type = Phrase; - } else if ( tokens[0] == "stk" ) { - key.type = Stack; - } else if ( tokens[0] == "btn" ) { - key.type = Between; - } else { - UTIL_THROW2("Flawed sparse reordering feature key"); - } - - // side - if ( tokens[1] == "src" ) { - key.side = Source; - } else if ( tokens[1] == "tgt" ) { - key.side = Target; - } else { - UTIL_THROW2("Flawed sparse reordering feature key"); - } - - // position - if ( tokens[2] == "first" ) { - key.position = First; - } else if ( tokens[2] == "last" ) { - key.position = Last; - } else { - UTIL_THROW2("Flawed sparse reordering feature key"); - } - - // wordListId - std::string& wordListId = tokens[3]; - - // cluster/word - if (starts_with(tokens[4], "cluster_")) { - key.isCluster = true; - } else { - key.isCluster = false; - } - - - - -buf << kSep; - - if (isCluster) buf << "cluster_"; - buf << word->GetString(); - -buf << kSep; - - buf << reoType; - - - name = buf.str(); - return name; -} -*/ } //namespace diff --git a/moses/FF/LexicalReordering/SparseReordering.h b/moses/FF/LexicalReordering/SparseReordering.h index 1aee1c4da..958ce998b 100644 --- a/moses/FF/LexicalReordering/SparseReordering.h +++ b/moses/FF/LexicalReordering/SparseReordering.h @@ -122,7 +122,6 @@ private: void ReadClusterMap(const std::string& filename, const std::string& id, SparseReorderingFeatureKey::Side side, std::vector* pClusterMaps); void PreCalculateFeatureNames(size_t index, const std::string& id, SparseReorderingFeatureKey::Side side, const Factor* factor, bool isCluster); void ReadWeightMap(const std::string& filename); -// const SparseReorderingFeatureKey FeatureKeyFromString(std::string& name) const; void AddFeatures( SparseReorderingFeatureKey::Type type, SparseReorderingFeatureKey::Side side, From 10bd94212716859f028c08f988649062679c6250 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Fri, 24 Apr 2015 20:00:07 +0700 Subject: [PATCH 53/53] Seed the randomizer once, not every time. This bit of mira code used to re-seed the randomizer on every call, instead of just once on startup. The result of time(NULL) was used as a seed, meaning that every such call to the randomizer within the same second would return the same value. --- contrib/mira/Main.cpp | 2 ++ contrib/mira/Main.h | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/contrib/mira/Main.cpp b/contrib/mira/Main.cpp index abf92b598..acc2f8886 100644 --- a/contrib/mira/Main.cpp +++ b/contrib/mira/Main.cpp @@ -46,6 +46,7 @@ namespace mpi = boost::mpi; #include "moses/FF/PhrasePairFeature.h" #include "moses/FF/WordPenaltyProducer.h" #include "moses/LM/Base.h" +#include "util/random.hh" using namespace Mira; using namespace std; @@ -54,6 +55,7 @@ namespace po = boost::program_options; int main(int argc, char** argv) { + util::rand_init(); size_t rank = 0; size_t size = 1; #ifdef MPI_ENABLE diff --git a/contrib/mira/Main.h b/contrib/mira/Main.h index ffc6d0d4e..b8faedae7 100644 --- a/contrib/mira/Main.h +++ b/contrib/mira/Main.h @@ -38,10 +38,6 @@ template bool from_string(T& t, const std::string& s, std::ios_base& ( struct RandomIndex { ptrdiff_t operator()(ptrdiff_t max) { - // TODO: Don't seed the randomizer here. If this function gets called - // multiple times in the same second, it will return the same value on - // each of those calls. - util::rand_init(); return util::rand_excl(max); } };